Merged PR 178588: Side channel JS SDK

From the end user's perspective, they will need to provide a third parameter to `powerbi.embed()` request.
The new input may be extended in the future to support other event hooks.

```javascript
powerbi.embed(..., config, { preQueryCallback: provideContext })
```
The code in `service.ts` is similar to the event listeners we set in the [constructor](https://powerbi.visualstudio.com/Embedded/_git/powerbi-javascript?path=%2Fsrc%2Fservice.ts&version=GBmaster&line=168&lineEnd=168&lineStartColumn=5&lineEndColumn=21&lineStyle=plain&_a=contents)
This commit is contained in:
Shahak Yosef 2021-07-29 11:19:36 +00:00
Родитель 51fbf9f652
Коммит 2a689943d4
10 изменённых файлов: 131 добавлений и 51 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -9,3 +9,4 @@ dist/powerbi.js.map
*.js.map
package-lock.json
demo/package-lock.json
.vscode

6
.vscode/settings.json поставляемый
Просмотреть файл

@ -1,6 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": false
}

29
.vscode/tasks.json поставляемый
Просмотреть файл

@ -1,29 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "npm",
"isShellCommand": true,
"showOutput": "always",
"suppressTaskName": true,
"tasks": [
{
"taskName": "build",
"args": [
"run",
"build"
],
"isBuildCommand": true
},
{
"taskName": "test",
"args": [
"run",
"test",
"--",
"--chrome"
],
"isTestCommand": true
}
]
}

6
dist/powerbi-client.d.ts поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
// powerbi-client v2.18.3
// powerbi-client v2.18.4
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
declare module "config" {
@ -2127,6 +2127,10 @@ declare module "service" {
* @hidden
*/
private embedExisting;
/**
* @hidden
*/
private registerApplicationContextHook;
/**
* Adds an event handler for DOMContentLoaded, which searches the DOM for elements that have the 'powerbi-embed-url' attribute,
* and automatically attempts to embed a powerbi component based on information from other powerbi-* attributes.

89
dist/powerbi.js поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
// powerbi-client v2.18.3
// powerbi-client v2.18.4
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
(function webpackUniversalModuleDefinition(root, factory) {
@ -290,7 +290,7 @@ return /******/ (function(modules) { // webpackBootstrap
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
// powerbi-models v1.9.2
// powerbi-models v1.9.3
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
(function webpackUniversalModuleDefinition(root, factory) {
@ -7128,7 +7128,7 @@ exports.BookmarksManager = BookmarksManager;
Object.defineProperty(exports, "__esModule", { value: true });
/** @ignore */ /** */
var config = {
version: '2.18.3',
version: '2.18.4',
type: 'js'
};
exports.default = config;
@ -7519,6 +7519,7 @@ var Embed = /** @class */ (function () {
* @hidden
*/
function Embed(service, element, config, iframe, phasedRender, isBootstrap) {
var _a;
/** @hidden */
this.allowedEvents = [];
if (util_1.autoAuthInEmbedUrl(config.embedUrl)) {
@ -7533,12 +7534,14 @@ var Embed = /** @class */ (function () {
this.embedtype = config.type.toLowerCase();
this.commands = [];
this.groups = [];
var registerQueryCallback = !!((_a = config.eventHooks) === null || _a === void 0 ? void 0 : _a.applicationContextProvider);
delete config.eventHooks;
this.populateConfig(config, isBootstrap);
if (this.embedtype === 'create') {
this.setIframe(false /* set EventListener to call create() on 'load' event*/, phasedRender, isBootstrap);
this.setIframe(false /* set EventListener to call create() on 'load' event*/, phasedRender, isBootstrap, registerQueryCallback);
}
else {
this.setIframe(true /* set EventListener to call load() on 'load' event*/, phasedRender, isBootstrap);
this.setIframe(true /* set EventListener to call load() on 'load' event*/, phasedRender, isBootstrap, registerQueryCallback);
}
}
/**
@ -8012,11 +8015,13 @@ var Embed = /** @class */ (function () {
*
* @hidden
*/
Embed.prototype.setIframe = function (isLoad, phasedRender, isBootstrap) {
Embed.prototype.setIframe = function (isLoad, phasedRender, isBootstrap, registerQueryCallback) {
var _this = this;
if (!this.iframe) {
var iframeContent = document.createElement("iframe");
var embedUrl = this.config.uniqueId ? util_1.addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl;
if (!isBootstrap && registerQueryCallback)
embedUrl = util_1.addParamToUrl(embedUrl, "registerQueryCallback", "true");
iframeContent.style.width = '100%';
iframeContent.style.height = '100%';
iframeContent.setAttribute("src", embedUrl);
@ -10363,6 +10368,42 @@ exports.Report = Report;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Service = void 0;
var embed_1 = __webpack_require__(/*! ./embed */ "./src/embed.ts");
@ -10518,6 +10559,7 @@ var Service = /** @class */ (function () {
*/
Service.prototype.embed = function (element, config) {
if (config === void 0) { config = {}; }
this.registerApplicationContextHook(config);
return this.embedInternal(element, config);
};
/**
@ -10532,6 +10574,7 @@ var Service = /** @class */ (function () {
*/
Service.prototype.load = function (element, config) {
if (config === void 0) { config = {}; }
this.registerApplicationContextHook(config);
return this.embedInternal(element, config, /* phasedRender */ true, /* isBootstrap */ false);
};
/**
@ -10636,6 +10679,40 @@ var Service = /** @class */ (function () {
component.load(phasedRender);
return component;
};
/**
* @hidden
*/
Service.prototype.registerApplicationContextHook = function (config) {
var _this = this;
var _a;
var applicationContextProvider = (_a = config === null || config === void 0 ? void 0 : config.eventHooks) === null || _a === void 0 ? void 0 : _a.applicationContextProvider;
if (!applicationContextProvider) {
return;
}
if (typeof applicationContextProvider !== 'function') {
throw new Error("applicationContextProvider must be a function");
}
this.router.post("preQuery", function (req, _res) { return __awaiter(_this, void 0, void 0, function () {
var result, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, applicationContextProvider(req.body)];
case 1:
result = _a.sent();
_res.send(200, result);
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
_res.send(400, null);
console.error(error_1);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
}); });
};
/**
* Adds an event handler for DOMContentLoaded, which searches the DOM for elements that have the 'powerbi-embed-url' attribute,
* and automatically attempts to embed a powerbi component based on information from other powerbi-* attributes.

4
dist/powerbi.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "powerbi-client",
"version": "2.18.3",
"version": "2.18.4",
"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",
"types": "dist/powerbi-client.d.ts",
@ -81,7 +81,7 @@
},
"dependencies": {
"http-post-message": "^0.2",
"powerbi-models": "^1.9.2",
"powerbi-models": "^1.9.3",
"powerbi-router": "^0.1",
"window-post-message-proxy": "^0.2"
},

Просмотреть файл

@ -3,7 +3,7 @@
/** @ignore *//** */
const config = {
version: '2.18.3',
version: '2.18.4',
type: 'js'
};

Просмотреть файл

@ -227,12 +227,15 @@ export abstract class Embed {
this.commands = [];
this.groups = [];
const registerQueryCallback = !!(<IEmbedConfiguration>config).eventHooks?.applicationContextProvider;
delete (<IEmbedConfiguration>config).eventHooks;
this.populateConfig(config, isBootstrap);
if (this.embedtype === 'create') {
this.setIframe(false /* set EventListener to call create() on 'load' event*/, phasedRender, isBootstrap);
this.setIframe(false /* set EventListener to call create() on 'load' event*/, phasedRender, isBootstrap, registerQueryCallback);
} else {
this.setIframe(true /* set EventListener to call load() on 'load' event*/, phasedRender, isBootstrap);
this.setIframe(true /* set EventListener to call load() on 'load' event*/, phasedRender, isBootstrap, registerQueryCallback);
}
}
@ -702,10 +705,14 @@ export abstract class Embed {
*
* @hidden
*/
private setIframe(isLoad: boolean, phasedRender?: boolean, isBootstrap?: boolean): void {
private setIframe(isLoad: boolean, phasedRender?: boolean, isBootstrap?: boolean, registerQueryCallback?: boolean): void {
if (!this.iframe) {
const iframeContent = document.createElement("iframe");
const embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl;
let embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl;
if (!isBootstrap && registerQueryCallback)
embedUrl = addParamToUrl(embedUrl, "registerQueryCallback", "true");
iframeContent.style.width = '100%';
iframeContent.style.height = '100%';
iframeContent.setAttribute("src", embedUrl);

Просмотреть файл

@ -300,6 +300,7 @@ export class Service implements IService {
* @returns {Embed}
*/
embed(element: HTMLElement, config: IComponentEmbedConfiguration | IEmbedConfigurationBase = {}): Embed {
this.registerApplicationContextHook(config as IEmbedConfiguration);
return this.embedInternal(element, config);
}
@ -314,6 +315,7 @@ export class Service implements IService {
* @returns {Embed}
*/
load(element: HTMLElement, config: IComponentEmbedConfiguration | IEmbedConfigurationBase = {}): Embed {
this.registerApplicationContextHook(config as IEmbedConfiguration);
return this.embedInternal(element, config, /* phasedRender */ true, /* isBootstrap */ false);
}
@ -440,6 +442,30 @@ export class Service implements IService {
return component;
}
/**
* @hidden
*/
private registerApplicationContextHook(config: IEmbedConfiguration): void {
const applicationContextProvider = config?.eventHooks?.applicationContextProvider;
if (!applicationContextProvider) {
return;
}
if (typeof applicationContextProvider !== 'function') {
throw new Error("applicationContextProvider must be a function");
}
this.router.post(`preQuery`, async (req, _res) => {
try {
let result = await applicationContextProvider(req.body);
_res.send(200, result);
} catch (error) {
_res.send(400, null);
console.error(error);
}
});
}
/**
* Adds an event handler for DOMContentLoaded, which searches the DOM for elements that have the 'powerbi-embed-url' attribute,
* and automatically attempts to embed a powerbi component based on information from other powerbi-* attributes.