diff --git a/demo/code-demo/scripts/codesamples.js b/demo/code-demo/scripts/codesamples.js index af07529d..ef57249e 100644 --- a/demo/code-demo/scripts/codesamples.js +++ b/demo/code-demo/scripts/codesamples.js @@ -7,6 +7,7 @@ // ---- Embed Code ---------------------------------------------------- function _Embed_BasicEmbed() { + // Read embed application token from textbox var txtAccessToken = $('#txtAccessToken').val(); @@ -16,6 +17,13 @@ function _Embed_BasicEmbed() { // Read report Id from textbox var txtEmbedReportId = $('#txtEmbedReportId').val(); + // Get models. models contains enums that can be used. + var models = window['powerbi-client'].models; + + // Embed report in View mode or Edit mode based or checkbox value. + var checked = $('#viewModeCheckbox')[0].checked; + var viewMode = checked ? models.ViewMode.Edit : models.ViewMode.View; + // Embed configuration used to describe the what and how to embed. // This object is used when calling powerbi.embed. // This also includes settings and options such as filters. @@ -25,13 +33,15 @@ function _Embed_BasicEmbed() { accessToken: txtAccessToken, embedUrl: txtEmbedUrl, id: txtEmbedReportId, + permissions: models.Permissions.All /*gives maximum permissions*/, + viewMode: viewMode, settings: { filterPaneEnabled: true, navContentPaneEnabled: true } }; - // Grab the reference to the div HTML element that will host the report. + // Get a reference to the embedded report HTML element var reportContainer = $('#reportContainer')[0]; // Embed the report and display it within the div container. @@ -44,6 +54,11 @@ function _Embed_BasicEmbed() { report.on("loaded", function() { Log.logText("Loaded"); }); + report.on("error", function(event) { + Log.log(event.detail); + + report.off("error"); + }); } function _Embed_EmbedWithDefaultFilter() { @@ -77,11 +92,53 @@ function _Embed_EmbedWithDefaultFilter() { powerbi.embed(reportContainer, embedConfiguration); } +function _Embed_Create() { + // Read embed application token from textbox + var txtAccessToken = $('#txtAccessToken').val(); + + // Read embed URL from textbox + var txtEmbedUrl = $('#txtReportEmbed').val(); + + // Read dataset Id from textbox + var txtEmbedDatasetId = $('#txtEmbedDatasetId').val(); + + // Embed create configuration used to describe the what and how to create report. + // This object is used when calling powerbi.createReport. + var embedCreateConfiguration = { + accessToken: txtAccessToken, + embedUrl: txtEmbedUrl, + datasetId: txtEmbedDatasetId, + }; + + // Grab the reference to the div HTML element that will host the report + var reportContainer = $('#reportContainer')[0]; + + // Create report + var report = powerbi.createReport(reportContainer, embedCreateConfiguration); + + // Report.off removes a given event handler if it exists. + report.off("loaded"); + + // Report.on will add an event handler which prints to Log window. + report.on("loaded", function() { + Log.logText("Loaded"); + }); + + report.on("error", function(event) { + Log.log(event.detail); + + report.off("error"); + }); +} + // ---- Report Operations ---------------------------------------------------- function _Report_GetId() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Retrieve the report id. var reportId = report.getId(); @@ -96,8 +153,11 @@ function _Report_UpdateSettings() { filterPaneEnabled: false }; + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Update the settings by passing in the new settings you have configured. report.updateSettings(newSettings) @@ -110,8 +170,11 @@ function _Report_UpdateSettings() { } function _Report_GetPages() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Retrieve the page collection and loop through to collect the // page name and display name of each page and display the value. @@ -128,8 +191,11 @@ function _Report_GetPages() { } function _Report_SetPage() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // setPage will change the selected view to the page you indicate. // This is the actual page name not the display name. @@ -153,8 +219,11 @@ function _Report_SetPage() { } function _Report_GetFilters() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Get the filters applied to the report. report.getFilters() @@ -179,8 +248,11 @@ function _Report_SetFilters() { values: ["Lindseys"] }; + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Set the filter for the report. // Pay attention that setFilters receives an array. @@ -194,8 +266,11 @@ function _Report_SetFilters() { } function _Report_RemoveFilters() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Remove the filters currently applied to the report. report.removeFilters() @@ -208,8 +283,11 @@ function _Report_RemoveFilters() { } function _Report_PrintCurrentReport() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Trigger the print dialog for your browser. report.print() @@ -222,8 +300,11 @@ function _Report_PrintCurrentReport() { } function _Report_Reload() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Reload the displayed report report.reload() @@ -236,26 +317,99 @@ function _Report_Reload() { } function _Report_FullScreen() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Displays the report in full screen mode. report.fullscreen(); } function _Report_ExitFullScreen() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Exits full screen mode. report.exitFullscreen(); } +function _Report_switchModeEdit() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + + // Get a reference to the embedded report. + report = powerbi.get(reportContainer); + + // Switch to edit mode. + report.switchMode("edit"); +} + +function _Report_switchModeView() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + + // Get a reference to the embedded report. + report = powerbi.get(reportContainer); + + // Switch to view mode. + report.switchMode("view"); +} + +function _Report_save() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + + // Get a reference to the embedded report. + report = powerbi.get(reportContainer); + + // Save report + report.save(); + + // report.on will add an event handler which prints to Log window. + report.on("saved", function(event) { + Log.log(event.detail); + + // report.off removes a given event handler if it exists. + report.off("saved"); + }); +} + +function _Report_saveAs() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + + // Get a reference to the embedded report. + report = powerbi.get(reportContainer); + + var saveAsParameters = { + name: "newReport" + }; + + // SaveAs report + report.saveAs(saveAsParameters); + + // report.on will add an event handler which prints to Log window. + report.on("saved", function(event) { + Log.log(event.detail); + + // report.off removes a given event handler if it exists. + report.off("saved"); + }); +} + // ---- Page Operations ---------------------------------------------------- function _Page_SetActive() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Retrieve the page collection, and then set the second page to be active. report.getPages() @@ -270,8 +424,11 @@ function _Page_SetActive() { } function _Page_GetFilters() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Retrieve the page collection and get the filters for the first page. report.getPages() @@ -290,8 +447,11 @@ function _Page_GetFilters() { } function _Page_SetFilters() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Build the filter you want to use. For more information, see Constructing // Filters in https://github.com/Microsoft/PowerBI-JavaScript/wiki/Filters. @@ -323,8 +483,11 @@ function _Page_SetFilters() { } function _Page_RemoveFilters() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Retrieve the page collection and remove the filters for the first page. report.getPages() @@ -345,8 +508,11 @@ function _Page_RemoveFilters() { // ---- Event Listener ---------------------------------------------------- function _Events_PageChanged() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Report.off removes a given event listener if it exists. report.off("pageChanged"); @@ -364,8 +530,11 @@ function _Events_PageChanged() { } function _Events_DataSelected() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + // Get a reference to the embedded report. - report = powerbi.embeds[0]; + report = powerbi.get(reportContainer); // Report.off removes a given event listener if it exists. report.off("dataSelected"); @@ -380,4 +549,25 @@ function _Events_DataSelected() { // For example, a bar in a bar chart. You should see an entry in the Log window. Log.logText("Select data to see events in Log window."); +} + +function _Events_SaveAsTriggered() { + // Get a reference to the embedded report HTML element + var reportContainer = $('#reportContainer')[0]; + + // Get a reference to the embedded report. + report = powerbi.get(reportContainer); + + // Report.off removes a given event listener if it exists. + report.off("saveAsTriggered"); + + // Report.on will add an event listener. + report.on("saveAsTriggered", function(event) { + Log.log(event); + }); + + // Select Run and then select SaveAs. + // You should see an entry in the Log window. + + Log.logText("Select SaveAs to see events in Log window."); } \ No newline at end of file diff --git a/demo/code-demo/scripts/report.js b/demo/code-demo/scripts/report.js index 8b83e877..6ecd4b6d 100644 --- a/demo/code-demo/scripts/report.js +++ b/demo/code-demo/scripts/report.js @@ -54,3 +54,22 @@ function OpenInteractStep() { LoadCodeArea("#embedCodeDiv", _Report_GetId); }); } + +function OpenCreateStep() { + $("#steps-auth a").removeClass(active_class); + $('#steps-embed a').addClass(active_class); + $('#steps-interact a').removeClass(active_class); + + $("#steps-auth .step-div").removeClass(active_div); + $('#steps-embed .step-div').addClass(active_div); + $('#steps-interact .step-div').removeClass(active_div); + + // Hide Embed view in authorization step. + $("#authorize-step-wrapper").hide(); + $("#embed-and-interact-steps-wrapper").show(); + + $("#settings").load("settings_create.html", function() { + SetTextBoxesFromSessionOrUrlParam("#txtAccessToken", "#txtReportEmbed", "#txtEmbedDatasetId"); + LoadCodeArea("#embedCodeDiv", _Embed_Create); + }); +} \ No newline at end of file diff --git a/demo/code-demo/scripts/step_authorize.js b/demo/code-demo/scripts/step_authorize.js index 2e1e4cad..d28d1f02 100644 --- a/demo/code-demo/scripts/step_authorize.js +++ b/demo/code-demo/scripts/step_authorize.js @@ -1,12 +1,14 @@ function OpenEmbedStepWithSampleValues(accessToken, embedUrl, reportId) { - SetSession(SessionKeys.AccessToken, accessToken); - SetSession(SessionKeys.EmbedUrl, embedUrl); - SetSession(SessionKeys.EmbedId, reportId); - + setSession(accessToken, embedUrl, reportId); OpenEmbedStep(); } +function OpenCleanEmbedStep() +{ + OpenEmbedStepWithSampleValues("","",""); +} + function OpenEmbedStepWithSample() { var staticReportUrl = 'https://powerbi-embed-api.azurewebsites.net/api/reports/c52af8ab-0468-4165-92af-dc39858d66ad'; fetch(staticReportUrl).then(function (response) { @@ -20,4 +22,31 @@ function OpenEmbedStepWithSample() { var accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXIiOiIwLjIuMCIsIndjbiI6IlBvd2VyQmlBenVyZVNhbXBsZXMiLCJ3aWQiOiJmODFjMTk2Ni1lZGVlLTQxMWItOGY4YS1mODQ0NjAxOWIwNDQiLCJyaWQiOiJjNTJhZjhhYi0wNDY4LTQxNjUtOTJhZi1kYzM5ODU4ZDY2YWQiLCJpc3MiOiJQb3dlckJJU0RLIiwiYXVkIjoiaHR0cHM6Ly9hbmFseXNpcy53aW5kb3dzLm5ldC9wb3dlcmJpL2FwaSIsImV4cCI6MTg5MzQ0ODgwMCwibmJmIjoxNDgxMDM3MTY5fQ.m4SwqmRWA9rJgfl72lEQ_G-Ijpw9Up5YwmBOfXi00YU"; OpenEmbedStepWithSampleValues(accessToken, embedUrl, reportId); +} + +function OpenEmbedStepCreateWithSampleValues(accessToken, embedUrl, datasetId) +{ + setSession(accessToken, embedUrl, datasetId); + OpenCreateStep(); +} + +function OpenCleanEmbedStepCreate() +{ + OpenEmbedStepCreateWithSampleValues("","",""); +} + +function OpenEmbedStepCreateWithSample() { + // Default values - report with embed token which expires on 1/1/2030. + var embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportEmbedEditingEnabled=true'; + var datasetId = '8f94aa87-a12b-4afa-9ff3-a0f78cd434b9'; + var accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3Y24iOiJEYmctV0FCSS1QQUFTLTEtU0NVUyIsIndpZCI6IjhhMGNlZTNlLTc3ZmEtNGM1Ny1hZTQ4LWM4NzliOTkwMjQyNSIsImRpZCI6IjhmOTRhYTg3LWExMmItNGFmYS05ZmYzLWEwZjc4Y2Q0MzRiOSIsInZlciI6IjAuMi4wIiwidHlwZSI6ImVtYmVkIiwic2NwIjoiRGF0YXNldC5SZWFkIiwiaXNzIjoiUG93ZXJCSVNESyIsImF1ZCI6Imh0dHBzOi8vYW5hbHlzaXMud2luZG93cy5uZXQvcG93ZXJiaS9hcGkiLCJleHAiOjE0OTU1MzE5MjEsIm5iZiI6MTQ4Njg4ODMyMX0.Lzug-8hFwPEWNgCJovk338Fc6Y6lrAZOcOruDRzT-Qw"; + + OpenEmbedStepCreateWithSampleValues(accessToken, embedUrl, datasetId); +} + +function setSession(accessToken, embedUrl, datasetId) +{ + SetSession(SessionKeys.AccessToken, accessToken); + SetSession(SessionKeys.EmbedUrl, embedUrl); + SetSession(SessionKeys.EmbedId, datasetId); } \ No newline at end of file diff --git a/demo/code-demo/scripts/step_embed.js b/demo/code-demo/scripts/step_embed.js index 1149557d..823c6249 100644 --- a/demo/code-demo/scripts/step_embed.js +++ b/demo/code-demo/scripts/step_embed.js @@ -70,3 +70,25 @@ function Events_PageChanged() { function Events_DataSelected() { SetCode(_Events_DataSelected); } + +function Events_SaveAsTriggered() { + SetCode(_Events_SaveAsTriggered); +} + +// ---- Edit and Save Operations ---------------------------------------------------- + +function Report_switchModeEdit() { + SetCode(_Report_switchModeEdit); +} + +function Report_switchModeView() { + SetCode(_Report_switchModeView); +} + +function Report_save() { + SetCode(_Report_save); +} + +function Report_saveAs() { + SetCode(_Report_saveAs); +} \ No newline at end of file diff --git a/demo/code-demo/scripts/step_interact.js b/demo/code-demo/scripts/step_interact.js index c6c85a11..5c7cc191 100644 --- a/demo/code-demo/scripts/step_interact.js +++ b/demo/code-demo/scripts/step_interact.js @@ -2,11 +2,13 @@ function OpenReportOperations() { $("#report-operations-div").show(); $("#page-operations-div").hide(); $("#events-operations-div").hide(); - + $("#editandsave-operations-div").hide(); + $("#report-operations-li").addClass('active'); $('#page-operations-li').removeClass('active'); $('#events-operations-li').removeClass('active'); - + $('#editandsave-operations-li').removeClass('active'); + $("#report-operations-div .function-ul li.active").click() $("#selected-catogory-button").html("Report operations"); @@ -17,10 +19,12 @@ function OpenPageOperations() { $("#page-operations-div").show(); $("#report-operations-div").hide(); $("#events-operations-div").hide(); + $("#editandsave-operations-div").hide(); $("#page-operations-li").addClass('active'); $('#report-operations-li').removeClass('active'); $('#events-operations-li').removeClass('active'); + $('#editandsave-operations-li').removeClass('active'); $("#page-operations-div .function-ul li.active").click(); @@ -32,17 +36,36 @@ function OpenEventOperations() { $("#page-operations-div").hide(); $("#report-operations-div").hide(); $("#events-operations-div").show(); + $("#editandsave-operations-div").hide(); $("#page-operations-li").removeClass('active'); $('#report-operations-li').removeClass('active'); $('#events-operations-li').addClass('active'); - + $('#editandsave-operations-li').removeClass('active'); + $("#events-operations-div .function-ul li.active").click(); $("#selected-catogory-button").html("Events Listener"); HideCategoriesList(); } +function OpenEditAndSaveOperations() { + $("#page-operations-div").hide(); + $("#report-operations-div").hide(); + $("#events-operations-div").hide(); + $("#editandsave-operations-div").show(); + + $("#page-operations-li").removeClass('active'); + $('#report-operations-li').removeClass('active'); + $('#events-operations-li').removeClass('active'); + $('#editandsave-operations-li').addClass('active'); + + $("#editandsave-operations-div .function-ul li.active").click(); + + $("#selected-catogory-button").html("Edit and save operations"); + HideCategoriesList(); +} + function HideCategoriesList() { $("#operations-ul-wrapper").hide(); } diff --git a/demo/code-demo/settings_create.html b/demo/code-demo/settings_create.html new file mode 100644 index 00000000..b62f884f --- /dev/null +++ b/demo/code-demo/settings_create.html @@ -0,0 +1,31 @@ +
+ +
+ +
+ +
+

Create Report

+ Fill in the fields below to get the code to create your report. +
+
+
+ Embed App Token +
+
+
+ Embed URL +
+
+
+ Dataset Id +
+
+
\ No newline at end of file diff --git a/demo/code-demo/settings_embed.html b/demo/code-demo/settings_embed.html index 8aded0e2..4010e078 100644 --- a/demo/code-demo/settings_embed.html +++ b/demo/code-demo/settings_embed.html @@ -1,4 +1,16 @@
+ +
+ +
+

Embed Report

Fill in the fields below to get the code to embed your report. @@ -16,4 +28,7 @@

Embed Report

Report Id
+
+ +
\ No newline at end of file diff --git a/demo/code-demo/settings_interact.html b/demo/code-demo/settings_interact.html index 4910f444..232a697b 100644 --- a/demo/code-demo/settings_interact.html +++ b/demo/code-demo/settings_interact.html @@ -16,6 +16,9 @@
  • Events listener
  • +
  • + Edit and save operations +
  • @@ -49,6 +52,15 @@ + + diff --git a/demo/code-demo/step_authorize.html b/demo/code-demo/step_authorize.html index 7a0aaeaa..72a627a1 100644 --- a/demo/code-demo/step_authorize.html +++ b/demo/code-demo/step_authorize.html @@ -7,6 +7,7 @@

    Sample Report

    +
    diff --git a/demo/code-demo/step_create.html b/demo/code-demo/step_create.html new file mode 100644 index 00000000..0101edae --- /dev/null +++ b/demo/code-demo/step_create.html @@ -0,0 +1,32 @@ + +
    + + + + + + + + + + + + + +
    Embed App Token
    Embed url + +
    Dataset Id
    + +

    Code

    +

    + + + + +
    + + diff --git a/demo/code-demo/style/style.css b/demo/code-demo/style/style.css index 72055dbb..fc4b4b86 100644 --- a/demo/code-demo/style/style.css +++ b/demo/code-demo/style/style.css @@ -48,6 +48,12 @@ a:hover, a:visited, a:link, a:active margin-bottom: 5px; } +#report-embed-checkbox input { + width: auto; + border: none; + margin-bottom: 5px; +} + #oldSample { display: block; float: right; @@ -490,3 +496,16 @@ a { position: relative; top: -1px; } + +.tabContainer { + margin-bottom: 5px; + padding-left: 0; +} + +.nav-tabs { + border-bottom: 0px; +} + +.checkbox.input { + width: auto; +} \ No newline at end of file diff --git a/package.json b/package.json index 64f9f897..dae19e02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powerbi-client", - "version": "2.2.6", + "version": "2.2.7", "description": "JavaScript library for embedding Power BI into your apps. Provides service which makes it easy to embed different types of components and an object model which allows easy interaction with these components such as changing pages, applying filters, and responding to data selection.", "main": "dist/powerbi.js", "typings": "dist/powerbi.d.ts", @@ -55,11 +55,11 @@ "jquery": "^2.2.3", "json-loader": "^0.5.4", "karma": "^0.13.19", - "karma-chrome-launcher": "^0.2.2", - "karma-coverage": "^0.5.3", + "karma-chrome-launcher": "^0.2.3", + "karma-coverage": "^0.5.5", "karma-firefox-launcher": "^1.0.0", - "karma-jasmine": "^0.3.7", - "karma-phantomjs-launcher": "^1.0.0", + "karma-jasmine": "^0.3.8", + "karma-phantomjs-launcher": "^1.0.2", "karma-spec-reporter": "0.0.23", "moment": "^2.14.1", "phantomjs-prebuilt": "^2.1.3", @@ -75,7 +75,7 @@ }, "dependencies": { "http-post-message": "^0.2.3", - "powerbi-models": "^0.10.1", + "powerbi-models": "^0.11.1", "powerbi-router": "^0.1.4", "window-post-message-proxy": "^0.2.4" }, diff --git a/src/config.ts b/src/config.ts index 6578ac26..9d82ea64 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,5 @@ const config = { - version: '2.2.6', + version: '2.2.7', type: 'js' }; diff --git a/src/create.ts b/src/create.ts new file mode 100644 index 00000000..49a140d0 --- /dev/null +++ b/src/create.ts @@ -0,0 +1,54 @@ +import * as service from './service'; +import * as models from 'powerbi-models'; +import * as embed from './embed'; + +export class Create extends embed.Embed { + + constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration) { + super(service, element, config); + } + + /** + * Gets the dataset ID from the first available location: createConfig or embed url. + * + * @returns {string} + */ + getId(): string { + const datasetId = (this.createConfig && this.createConfig.datasetId) ? this.createConfig.datasetId : Create.findIdFromEmbedUrl(this.config.embedUrl); + + if (typeof datasetId !== 'string' || datasetId.length === 0) { + throw new Error('Dataset id is required, but it was not found. You must provide an id either as part of embed configuration.'); + } + + return datasetId; + } + + /** + * Validate create report configuration. + */ + validate(config: models.IReportCreateConfiguration): models.IError[] { + return models.validateCreateReport(config); + } + + /** + * Adds the ability to get datasetId from url. + * (e.g. http://embedded.powerbi.com/appTokenReportEmbed?datasetId=854846ed-2106-4dc2-bc58-eb77533bf2f1). + * + * By extracting the ID we can ensure that the ID is always explicitly provided as part of the create configuration. + * + * @static + * @param {string} url + * @returns {string} + */ + static findIdFromEmbedUrl(url: string): string { + const datasetIdRegEx = /datasetId="?([^&]+)"?/ + const datasetIdMatch = url.match(datasetIdRegEx); + + let datasetId; + if (datasetIdMatch) { + datasetId = datasetIdMatch[1]; + } + + return datasetId; + } +} \ No newline at end of file diff --git a/src/embed.ts b/src/embed.ts index 16251118..9646da0c 100644 --- a/src/embed.ts +++ b/src/embed.ts @@ -39,6 +39,9 @@ export interface IEmbedConfiguration { pageName?: string; filters?: models.IFilter[]; pageView?: models.PageView; + datasetId?: string; + permissions?: models.Permissions; + viewMode?: models.ViewMode; } export interface IInternalEmbedConfiguration extends models.IReportLoadConfiguration { @@ -60,7 +63,7 @@ export interface IInternalEventHandler { * @class Embed */ export abstract class Embed { - static allowedEvents = ["loaded"]; + static allowedEvents = ["loaded", "saved", "rendered", "saveAsTriggered", "error", "dataSelected"]; static accessTokenAttribute = 'powerbi-access-token'; static embedUrlAttribute = 'powerbi-embed-url'; static nameAttribute = 'powerbi-name'; @@ -108,11 +111,23 @@ export abstract class Embed { */ config: IInternalEmbedConfiguration; + /** + * Gets or sets the configuration settings for creating report. + * + * @type {models.IReportCreateConfiguration} + */ + createConfig: models.IReportCreateConfiguration; + /** * Url used in the load request. */ loadPath: string; + /** + * Type of embed + */ + embeType: string; + /** * Creates an instance of Embed. * @@ -123,25 +138,78 @@ export abstract class Embed { * @param {HTMLElement} element * @param {IEmbedConfiguration} config */ - constructor(service: service.Service, element: HTMLElement, config: IEmbedConfiguration) { + constructor(service: service.Service, element: HTMLElement, config: IEmbedConfiguration, iframe?: HTMLIFrameElement) { Array.prototype.push.apply(this.allowedEvents, Embed.allowedEvents); this.eventHandlers = []; this.service = service; this.element = element; + this.iframe = iframe; + this.embeType = config.type.toLowerCase(); + + this.populateConfig(config); + + if(this.embeType === 'create'){ + this.setIframe(false/*set EventListener to call create() on 'load' event*/); + } else { + this.setIframe(true/*set EventListener to call load() on 'load' event*/); + } + } - // TODO: Change when Object.assign is available. - const settings = utils.assign({}, Embed.defaultSettings, config.settings); - this.config = utils.assign({ settings }, config); - this.config.accessToken = this.getAccessToken(service.accessToken); - this.config.embedUrl = this.getEmbedUrl(); - this.config.id = this.getId(); - this.config.uniqueId = this.getUniqueId(); + /** + * Sends createReport configuration data. + * + * ```javascript + * createReport({ + * datasetId: '5dac7a4a-4452-46b3-99f6-a25915e0fe55', + * accessToken: 'eyJ0eXA ... TaE2rTSbmg', + * ``` + * + * @param {models.IReportCreateConfiguration} config + * @returns {Promise} + */ + createReport(config: models.IReportCreateConfiguration): Promise { + const errors = this.validate(config); + if (errors) { + throw errors; + } + + return this.service.hpm.post("/report/create", config, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }, + response => { + throw response.body; + }); + } - const iframeHtml = ``; + /** + * Saves Report. + * + * @returns {Promise} + */ + save(): Promise { + return this.service.hpm.post('/report/save', null, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }) + .catch(response => { + throw response.body; + }); + } - this.element.innerHTML = iframeHtml; - this.iframe = this.element.childNodes[0]; - this.iframe.addEventListener('load', () => this.load(this.config), false); + /** + * SaveAs Report. + * + * @returns {Promise} + */ + saveAs(saveAsParameters: models.ISaveAsParameters): Promise { + return this.service.hpm.post('/report/saveAs', saveAsParameters, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }) + .catch(response => { + throw response.body; + }); } /** @@ -260,7 +328,22 @@ export abstract class Embed { reload(): Promise { return this.load(this.config); } - + + /** + * Set accessToken. + * + * @returns {Promise} + */ + setAccessToken(accessToken: string): Promise { + return this.service.hpm.post('/report/token', accessToken, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }) + .catch(response => { + throw response.body; + }); + } + /** * Gets an access token from the first available location: config, attribute, global. * @@ -278,6 +361,33 @@ export abstract class Embed { return accessToken; } + /** + * Populate config for create and load + * + * @private + * @param {IEmbedConfiguration} + * @returns {void} + */ + private populateConfig(config: IEmbedConfiguration): void { + // TODO: Change when Object.assign is available. + const settings = utils.assign({}, Embed.defaultSettings, config.settings); + this.config = utils.assign({ settings }, config); + this.config.uniqueId = this.getUniqueId(); + this.config.embedUrl = this.getEmbedUrl(); + + if(this.embeType === 'create') { + this.createConfig = { + datasetId: config.datasetId || this.getId(), + accessToken: this.getAccessToken(this.service.accessToken), + settings: settings + } + } else { + this.config.id = this.getId(); + this.config.accessToken = this.getAccessToken(this.service.accessToken); + } + } + + /** * Gets an embed url from the first available location: options, attribute. * @@ -348,7 +458,24 @@ export abstract class Embed { } /** - * Validate load configuration. + * Validate load and create configuration. + */ + abstract validate(config: models.IReportLoadConfiguration | models.IDashboardLoadConfiguration | models.IReportCreateConfiguration): models.IError[]; + + /** + * Sets Iframe for embed */ - abstract validate(config: models.IReportLoadConfiguration | models.IDashboardLoadConfiguration): models.IError[]; + private setIframe(isLoad: boolean): void { + if(!this.iframe) { + const iframeHtml = ``; + this.element.innerHTML = iframeHtml; + this.iframe = this.element.childNodes[0]; + } + + if(isLoad){ + this.iframe.addEventListener('load', () => this.load(this.config), false); + } else { + this.iframe.addEventListener('load', () => this.createReport(this.createConfig), false); + } + } } \ No newline at end of file diff --git a/src/report.ts b/src/report.ts index 043d1030..d4c498c0 100644 --- a/src/report.ts +++ b/src/report.ts @@ -29,7 +29,7 @@ export interface IReportNode { * @implements {IFilterable} */ export class Report extends embed.Embed implements IReportNode, IFilterable { - static allowedEvents = ["rendered", "dataSelected", "filtersApplied", "pageChanged", "error"]; + static allowedEvents = ["filtersApplied", "pageChanged"]; static reportIdAttribute = 'powerbi-report-id'; static filterPaneEnabledAttribute = 'powerbi-settings-filter-pane-enabled'; static navContentPaneEnabledAttribute = 'powerbi-settings-nav-content-pane-enabled'; @@ -43,7 +43,7 @@ export class Report extends embed.Embed implements IReportNode, IFilterable { * @param {HTMLElement} element * @param {embed.IEmbedConfiguration} config */ - constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration) { + constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration, iframe?: HTMLIFrameElement) { const filterPaneEnabled = (config.settings && config.settings.filterPaneEnabled) || !(element.getAttribute(Report.filterPaneEnabledAttribute) === "false"); const navContentPaneEnabled = (config.settings && config.settings.navContentPaneEnabled) || !(element.getAttribute(Report.navContentPaneEnabledAttribute) === "false"); const settings = utils.assign({ @@ -52,7 +52,7 @@ export class Report extends embed.Embed implements IReportNode, IFilterable { }, config.settings); const configCopy = utils.assign({ settings }, config); - super(service, element, configCopy); + super(service, element, configCopy, iframe); this.loadPath = "/report/load"; Array.prototype.push.apply(this.allowedEvents, Report.allowedEvents); } @@ -263,4 +263,20 @@ export class Report extends embed.Embed implements IReportNode, IFilterable { validate(config: models.IReportLoadConfiguration): models.IError[] { return models.validateReportLoad(config); } + + /** + * Switch Report view mode. + * + * @returns {Promise} + */ + switchMode(viewMode: models.ViewMode): Promise { + let url = '/report/switchMode/' + viewMode; + return this.service.hpm.post(url, null, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }) + .catch(response => { + throw response.body; + }); + } } diff --git a/src/service.ts b/src/service.ts index de4d7c69..8184efda 100644 --- a/src/service.ts +++ b/src/service.ts @@ -1,5 +1,6 @@ import * as embed from './embed'; import { Report } from './report'; +import { Create } from './create'; import { Dashboard } from './dashboard'; import { Tile } from './tile'; import { Page } from './page'; @@ -158,6 +159,22 @@ export class Service implements IService { } } + /** + * Creates new report + * @param {HTMLElement} element + * @param {embed.IEmbedConfiguration} [config={}] + * @returns {embed.Embed} + */ + createReport(element: HTMLElement, config: embed.IEmbedConfiguration): embed.Embed { + config.type = 'create'; + let powerBiElement = element; + const component = new Create(this, powerBiElement, config); + powerBiElement.powerBiEmbed = component; + this.embeds.push(component); + + return component; + } + /** * TODO: Add a description here * @@ -244,6 +261,19 @@ export class Service implements IService { * then we can call the embedNew function which would allow setting the proper embedUrl and construction of object based on the new type. */ if (typeof config.type === "string" && config.type !== component.config.type) { + + /** + * When loading report after create we want to use existing Iframe to optimize load period + */ + if(config.type === "report" && component.config.type === "create") { + const report = new Report(this, element, config, element.powerBiEmbed.iframe); + report.load(config); + element.powerBiEmbed = report; + this.embeds.push(report); + + return report; + } + throw new Error(`Embedding on an existing element with a different type than the previous embed object is not supported. Attempted to embed using config ${JSON.stringify(config)} on element ${element.outerHTML}, but the existing element contains an embed of type: ${this.config.type} which does not match the new type: ${config.type}`); } @@ -321,8 +351,7 @@ export class Service implements IService { */ private handleEvent(event: IEvent): void { const embed = utils.find(embed => { - return (embed.config.type === event.type - && embed.config.uniqueId === event.id); + return (embed.config.uniqueId === event.id); }, this.embeds); if (embed) { diff --git a/test/test.spec.ts b/test/test.spec.ts index f7f3ee3a..3353d3c5 100644 --- a/test/test.spec.ts +++ b/test/test.spec.ts @@ -1,6 +1,7 @@ import * as service from '../src/service'; import * as embed from '../src/embed'; import * as report from '../src/report'; +import * as create from '../src/create'; import * as dashboard from '../src/dashboard'; import * as page from '../src/page'; import * as Wpmp from 'window-post-message-proxy'; @@ -197,6 +198,36 @@ describe('service', function () { expect(attemptEmbed).toThrowError(Error); }); + it('if Create is already embedded in element re-use the existing component by calling load with the new information', function () { + // Arrange + const $element = $('
    ') + .appendTo('#powerbi-fixture'); + + const testConfiguration = { + accessToken: "fakeAccessToken", + embedUrl: 'fakeUrl', + id: 'report2', + type: 'report' + }; + + const createConfig: embed.IEmbedConfiguration = { + datasetId: "fakeDashboardId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl" + }; + + // Act + const component = powerbi.createReport($element[0], createConfig); + const component2 = powerbi.embed($element[0], testConfiguration); + const component3 = powerbi.get($element[0]); + + // Assert + //expect(component.createReport).toHaveBeenCalledWith(createConfig); + expect(component).toBeDefined(); + expect(component2).toBeDefined(); + expect(component2).toBe(component3); + }); + it('if attempting to embed without specifying an embed url, throw error', function () { // Arrange const component = $('
    ') @@ -422,6 +453,115 @@ describe('service', function () { powerbi.accessToken = originalToken; }); + describe('createReport', function () { + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const accessToken = 'ABC123'; + + it('if attempting to createReport without specifying an embed url, throw error', function () { + // Arrange + const component = $('
    ') + .appendTo('#powerbi-fixture'); + + // Act + const attemptCreate = () => { + powerbi.createReport(component[0], { embedUrl: null, accessToken: accessToken, datasetId: '123' }); + }; + + // Assert + expect(attemptCreate).toThrowError(Error); + }); + + it('if attempting to createReport without specifying an access token, throw error', function () { + // Arrange + const component = $('
    ') + .appendTo('#powerbi-fixture'); + + const originalToken = powerbi.accessToken; + powerbi.accessToken = undefined; + + // Act + const attemptCreate = () => { + powerbi.createReport(component[0], { embedUrl: embedUrl, accessToken: null, datasetId: '123' }); + }; + + // Assert + expect(attemptCreate).toThrowError(Error); + + // Cleanup + powerbi.accessToken = originalToken; + }); + + it('if attempting to createReport without specifying an datasetId, throw error', function () { + // Arrange + const $reportContainer = $(`
    `) + .appendTo('#powerbi-fixture'); + + // Act + const attemptCreate = () => { + powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); + }; + + // Assert + expect(attemptCreate).toThrowError(); + }); + + }); + + describe('findIdFromEmbedUrl of Create', function () { + it('should return value of datasetId query parameter in embedUrl', function () { + // Arrange + const testDatasetId = "ABC123"; + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; + + // Act + const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(datasetId).toEqual(testDatasetId); + }); + + it('should return undefinded if the datasetId parameter is not in the url', function () { + // Arrange + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed`; + + // Act + const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(datasetId).toBeUndefined(); + }); + + it('should get datasetId from configuration first', function () { + // Arrange + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=DIFFERENTID`; + const $reportContainer = $(`
    `) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken, datasetId: testDatasetId }); + + // Assert + expect(report.createConfig.datasetId).toEqual(testDatasetId); + }); + + it('should fallback to using datasetId from embedUrl if not supplied in create configuration', function () { + // Arrange + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; + const $reportContainer = $(`
    `) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); + + // Assert + expect(report.createConfig.datasetId).toEqual(testDatasetId); + }); + }); + describe('reports', function () { it('creates report iframe from embedUrl', function () { // Arrange @@ -776,6 +916,74 @@ describe('Protocol', function () { }); }); + describe('create', function () { + describe('report', function () { + it('POST /report/create returns 400 if the request is invalid', function (done) { + // Arrange + const testData = { + uniqueId: 'uniqueId', + create: { + datasetId: "fakeId", + accessToken: "fakeToken", + } + }; + + iframeLoaded + .then(() => { + spyApp.validateCreateReport.and.returnValue(Promise.reject(null)); + + // Act + hpm.post('/report/create', testData.create, { uid: testData.uniqueId }) + .then(() => { + expect(false).toBe(true); + spyApp.validateReportLoad.calls.reset(); + done(); + }) + .catch(response => { + // Assert + expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); + expect(response.statusCode).toEqual(400); + + // Cleanup + spyApp.validateCreateReport.calls.reset(); + done(); + }); + }); + }); + + it('POST /report/create returns 202 if the request is valid', function (done) { + // Arrange + const testData = { + create: { + datasetId: "fakeId", + accessToken: "fakeToken", + } + }; + + iframeLoaded + .then(() => { + spyApp.validateCreateReport.and.returnValue(Promise.resolve(null)); + // Act + hpm.post('/report/create', testData.create) + .then(response => { + // Assert + expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); + expect(response.statusCode).toEqual(202); + // Cleanup + spyApp.validateCreateReport.calls.reset(); + spyApp.reportLoad.calls.reset(); + done(); + }) + .catch(response => { + expect(false).toBe(true); + spyApp.validateCreateReport.calls.reset(); + done(); + }); + }); + }); + }); + }); + describe('load', function () { describe('report', function () { it('POST /report/load returns 400 if the request is invalid', function (done) { @@ -1202,6 +1410,92 @@ describe('Protocol', function () { }); }); + describe('switchMode', function () { + it('POST /report/switchMode returns 202 if the request is valid', function (done) { + // Arrange + iframeLoaded + .then(() => { + spyApp.switchMode.and.returnValue(Promise.resolve(null)); + // Act + hpm.post('/report/switchMode/Edit', null) + .then(response => { + // Assert + expect(spyApp.switchMode).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + // Cleanup + spyApp.switchMode.calls.reset(); + done(); + }); + }); + }); + }); + + describe('save', function () { + it('POST /report/save returns 202 if the request is valid', function (done) { + // Arrange + iframeLoaded + .then(() => { + spyApp.save.and.returnValue(Promise.resolve(null)); + // Act + hpm.post('/report/save', null) + .then(response => { + // Assert + expect(spyApp.save).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + // Cleanup + spyApp.save.calls.reset(); + done(); + }); + }); + }); + }); + + describe('saveAs', function () { + it('POST /report/saveAs returns 202 if the request is valid', function (done) { + // Arrange + let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; + + iframeLoaded + .then(() => { + spyApp.saveAs.and.returnValue(Promise.resolve(null)); + // Act + hpm.post('/report/saveAs', saveAsParameters) + .then(response => { + // Assert + expect(spyApp.saveAs).toHaveBeenCalled(); + expect(spyApp.saveAs).toHaveBeenCalledWith(saveAsParameters); + expect(response.statusCode).toEqual(202); + // Cleanup + spyApp.saveAs.calls.reset(); + done(); + }); + }); + }); + }); + + describe('setAccessToken', function () { + it('POST /report/token returns 202 if the request is valid', function (done) { + // Arrange + let accessToken: string = "fakeToken"; + + iframeLoaded + .then(() => { + spyApp.setAccessToken.and.returnValue(Promise.resolve(null)); + // Act + hpm.post('/report/token', accessToken) + .then(response => { + // Assert + expect(spyApp.setAccessToken).toHaveBeenCalled(); + expect(spyApp.setAccessToken).toHaveBeenCalledWith(accessToken); + expect(response.statusCode).toEqual(202); + // Cleanup + spyApp.saveAs.calls.reset(); + done(); + }); + }); + }); + }); + describe('filters (report level)', function () { it('GET /report/filters returns 200 with body as array of filters', function (done) { // Arrange @@ -1805,16 +2099,21 @@ describe('Protocol', function () { describe('SDK-to-HPM', function () { let $reportElement: JQuery; let $dashboardElement: JQuery; + let $createElement: JQuery; let iframe: HTMLIFrameElement; let dashboardIframe: HTMLIFrameElement; + let createIframe: HTMLIFrameElement; let powerbi: service.Service; let report: report.Report; + let create: create.Create; let dashboard: dashboard.Dashboard; let page1: page.Page; let uniqueId = 'uniqueId'; + let createUniqueId = 'uniqueId'; let dashboardUniqueId = 'uniqueId'; let embedConfiguration: embed.IEmbedConfiguration; let dashboardEmbedConfiguration: embed.IEmbedConfiguration; + let embedCreateConfiguration: embed.IEmbedConfiguration; beforeAll(function () { const spyHpmFactory: factories.IHpmFactory = () => { @@ -1832,6 +2131,8 @@ describe('SDK-to-HPM', function () { $reportElement = $(`
    `) .appendTo(document.body); + $createElement = $(`
    `) + .appendTo(document.body); $dashboardElement = $(`
    `) .appendTo(document.body); @@ -1842,6 +2143,11 @@ describe('SDK-to-HPM', function () { accessToken: 'fakeToken', embedUrl: iframeSrc }; + embedCreateConfiguration = { + datasetId: "fakeReportId", + accessToken: 'fakeToken', + embedUrl: iframeSrc + }; dashboardEmbedConfiguration = { type: "dashboard", id: "fakeDashboardId", @@ -1849,12 +2155,15 @@ describe('SDK-to-HPM', function () { embedUrl: iframeSrc }; report = powerbi.embed($reportElement[0], embedConfiguration); + create = powerbi.createReport($createElement[0], embedCreateConfiguration); dashboard = powerbi.embed($dashboardElement[0], dashboardEmbedConfiguration); page1 = new page.Page(report, 'xyz'); uniqueId = report.config.uniqueId; + createUniqueId = create.config.uniqueId; dashboardUniqueId = dashboard.config.uniqueId; iframe = $reportElement.find('iframe')[0]; + createIframe = $createElement.find('iframe')[0]; dashboardIframe = $dashboardElement.find('iframe')[0]; // Reset load handler @@ -2212,6 +2521,132 @@ describe('SDK-to-HPM', function () { }); }); + describe('switchMode', function () { + it('report.switchMode() sends POST /report/switchMode', function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.switchMode(models.ViewMode.Edit); + + // Assert + let url = '/report/switchMode/' + models.ViewMode.Edit; + expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, iframe.contentWindow); + }); + + it('report.switchMode() returns promise that resolves if the request is accepted', function (done) { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.switchMode(models.ViewMode.Edit) + .then(() => { + // Assert + let url = '/report/switchMode/' + models.ViewMode.Edit; + expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, iframe.contentWindow); + done(); + }); + }); + }); + + describe('save', function () { + it('report.save() sends POST /report/save', function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.save(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, iframe.contentWindow); + }); + + it('report.save() returns promise that resolves if the request is accepted', function (done) { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.save() + .then(() => { + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, iframe.contentWindow); + done(); + }); + }); + }); + + describe('saveAs', function () { + let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; + + it('report.saveAs() sends POST /report/saveAs', function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.saveAs(saveAsParameters); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, iframe.contentWindow); + }); + + it('report.saveAs() returns promise that resolves if the request is accepted', function (done) { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.saveAs(saveAsParameters) + .then(() => { + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, iframe.contentWindow); + done(); + }); + }); + }); + + describe('setAccessToken', function () { + let accessToken: string = "fakeToken"; + + it('report.setAccessToken() sends POST /report/token', function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.setAccessToken(accessToken); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/token', accessToken, { uid: uniqueId }, iframe.contentWindow); + }); + + it('report.setAccessToken() returns promise that resolves if the request is accepted', function (done) { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + report.setAccessToken(accessToken) + .then(() => { + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/token', accessToken, { uid: uniqueId }, iframe.contentWindow); + done(); + }); + }); + }); + describe('print', function () { it('report.print() sends POST /report/print', function () { // Arrange @@ -2336,6 +2771,81 @@ describe('SDK-to-HPM', function () { }); }); + describe('create', function () { + describe('createReport', function () { + it('create.createReport() sends POST /report/create with configuration in body', function () { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + create.createReport(testData.createConfiguration); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId }, createIframe.contentWindow); + }); + + it('create.createReport() returns promise that rejects with validation error if the create configuration is invalid', function (done) { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + errorResponse: { + body: { + message: "invalid configuration object" + } + } + }; + + spyHpm.post.and.returnValue(Promise.reject(testData.errorResponse)); + + // Act + create.createReport(testData.createConfiguration) + .catch(error => { + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId }, createIframe.contentWindow); + expect(error).toEqual(testData.errorResponse.body); + // Assert + done(); + }); + }); + + it('create.createReport() returns promise that resolves with null if create report was successful', function (done) { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + create.createReport(testData.createConfiguration) + .then(response => { + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId }, createIframe.contentWindow); + expect(response).toEqual(null); + // Assert + done(); + }); + }); + }); + }); + describe('dashboard', function () { describe('load', function () { it('dashboard.load() sends POST /dashboard/load with configuration in body', function () { diff --git a/test/utility/mockApp.ts b/test/utility/mockApp.ts index ae015a56..e9242dfb 100644 --- a/test/utility/mockApp.ts +++ b/test/utility/mockApp.ts @@ -20,6 +20,11 @@ export interface IApp { // Other print(): Promise; exportData(): Promise; + validateCreateReport(config: models.IReportCreateConfiguration): Promise; + switchMode(): Promise; + save(): Promise; + saveAs(saveAsParameters: models.ISaveAsParameters): Promise; + setAccessToken(accessToken: string): Promise; } export const mockAppSpyObj = { @@ -42,6 +47,11 @@ export const mockAppSpyObj = { // Other print: jasmine.createSpy("print").and.returnValue(Promise.resolve(null)), exportData: jasmine.createSpy("exportData").and.returnValue(Promise.resolve(null)), + validateCreateReport: jasmine.createSpy("validateCreateReport").and.callFake(models.validateCreateReport), + switchMode: jasmine.createSpy("switchMode").and.returnValue(Promise.resolve(null)), + save: jasmine.createSpy("save").and.returnValue(Promise.resolve(null)), + saveAs: jasmine.createSpy("saveAs").and.returnValue(Promise.resolve(null)), + setAccessToken: jasmine.createSpy("setAccessToken").and.returnValue(Promise.resolve(null)), reset() { mockAppSpyObj.dashboardLoad.calls.reset(); @@ -58,6 +68,11 @@ export const mockAppSpyObj = { mockAppSpyObj.validateFilter.calls.reset(); mockAppSpyObj.print.calls.reset(); mockAppSpyObj.exportData.calls.reset(); + mockAppSpyObj.validateCreateReport.calls.reset(); + mockAppSpyObj.switchMode.calls.reset(); + mockAppSpyObj.save.calls.reset(); + mockAppSpyObj.saveAs.calls.reset(); + mockAppSpyObj.setAccessToken.calls.reset(); } }; diff --git a/test/utility/mockEmbed.ts b/test/utility/mockEmbed.ts index 60f98160..0c05226e 100644 --- a/test/utility/mockEmbed.ts +++ b/test/utility/mockEmbed.ts @@ -65,6 +65,30 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win }); }); + /** + * Create Report + */ + router.post('/report/create', (req, res) => { + const uniqueId = req.headers['uid']; + const createConfig = req.body; + return app.validateCreateReport(createConfig) + .then(() => { + app.reportLoad(createConfig) + .then(() => { + const initiator = "sdk"; + hpm.post(`/reports/${uniqueId}/events/loaded`, { + initiator + }); + }, error => { + hpm.post(`/reports/${uniqueId}/events/error`, error); + }); + + res.send(202); + }, error => { + res.send(400, error); + }); + }); + /** * Report Embed */ @@ -244,5 +268,27 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win res.send(202); }); + router.post('report/switchMode/Edit', (req, res) => { + app.switchMode(); + res.send(202); + }); + + router.post('report/save', (req, res) => { + app.save(); + res.send(202); + }); + + router.post('report/saveAs', (req, res) => { + const settings = req.body; + app.saveAs(settings); + res.send(202); + }); + + router.post('report/token', (req, res) => { + const settings = req.body; + app.setAccessToken(settings); + res.send(202); + }); + return hpm; } \ No newline at end of file