Коммит
10d3360357
|
@ -11,3 +11,5 @@ package-lock.json
|
|||
demo/package-lock.json
|
||||
.vscode
|
||||
owners.txt
|
||||
test/util.spec.ts
|
||||
.config/tsaoptions.json
|
|
@ -1,4 +1,4 @@
|
|||
// powerbi-client v2.22.2
|
||||
// powerbi-client v2.23.1
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
declare module "config" {
|
||||
|
@ -12,6 +12,7 @@ declare module "config" {
|
|||
declare module "errors" {
|
||||
export const APINotSupportedForRDLError = "This API is currently not supported for RDL reports";
|
||||
export const EmbedUrlNotSupported = "Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL";
|
||||
export const invalidEmbedUrlErrorMessage: string;
|
||||
}
|
||||
declare module "util" {
|
||||
import { HttpPostMessage } from 'http-post-message';
|
||||
|
@ -124,6 +125,11 @@ declare module "util" {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
export function isCreate(embedType: string): boolean;
|
||||
/**
|
||||
* Checks if the embedUrl has an allowed power BI domain
|
||||
* @hidden
|
||||
*/
|
||||
export function validateEmbedUrl(embedUrl: string): boolean;
|
||||
}
|
||||
declare module "embed" {
|
||||
import * as models from 'powerbi-models';
|
||||
|
@ -151,6 +157,7 @@ declare module "embed" {
|
|||
export type ITileEmbedConfiguration = models.ITileEmbedConfiguration;
|
||||
export type IQnaEmbedConfiguration = models.IQnaEmbedConfiguration;
|
||||
export type IQuickCreateConfiguration = models.IQuickCreateConfiguration;
|
||||
export type IReportCreateConfiguration = models.IReportCreateConfiguration;
|
||||
export type ILocaleSettings = models.ILocaleSettings;
|
||||
export type IQnaSettings = models.IQnaSettings;
|
||||
export type IEmbedSettings = models.ISettings;
|
||||
|
@ -597,7 +604,7 @@ declare module "ifilterable" {
|
|||
}
|
||||
}
|
||||
declare module "visualDescriptor" {
|
||||
import { ExportDataType, FiltersOperations, ICloneVisualRequest, ICloneVisualResponse, IExportDataResult, IFilter, ISlicerState, ISortByVisualRequest, IVisualLayout, VisualContainerDisplayMode } from 'powerbi-models';
|
||||
import { ExportDataType, FiltersOperations, ICloneVisualRequest, ICloneVisualResponse, IExportDataResult, IFilter, ISlicerState, ISmartNarratives, ISortByVisualRequest, IVisualLayout, VisualContainerDisplayMode } from 'powerbi-models';
|
||||
import { IHttpPostMessageResponse } from 'http-post-message';
|
||||
import { IFilterable } from "ifilterable";
|
||||
import { IPageNode } from "page";
|
||||
|
@ -794,11 +801,21 @@ declare module "visualDescriptor" {
|
|||
* @returns {Promise<IHttpPostMessageResponse<void>>}
|
||||
*/
|
||||
resizeVisual(width: number, height: number): Promise<IHttpPostMessageResponse<void>>;
|
||||
/**
|
||||
* Get insights for single visual
|
||||
*
|
||||
* ```javascript
|
||||
* visual.getSmartNarrativeInsights();
|
||||
* ```
|
||||
*
|
||||
* @returns {Promise<ISmartNarratives>}
|
||||
*/
|
||||
getSmartNarrativeInsights(): Promise<ISmartNarratives>;
|
||||
}
|
||||
}
|
||||
declare module "page" {
|
||||
import { IHttpPostMessageResponse } from 'http-post-message';
|
||||
import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode, IPageBackground, IPageWallpaper } from 'powerbi-models';
|
||||
import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode, IPageBackground, IPageWallpaper, ISmartNarratives } from 'powerbi-models';
|
||||
import { IFilterable } from "ifilterable";
|
||||
import { IReportNode } from "report";
|
||||
import { VisualDescriptor } from "visualDescriptor";
|
||||
|
@ -894,6 +911,16 @@ declare module "page" {
|
|||
* @hidden
|
||||
*/
|
||||
constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize, background?: IPageBackground, wallpaper?: IPageWallpaper);
|
||||
/**
|
||||
* Get insights for report page
|
||||
*
|
||||
* ```javascript
|
||||
* page.getSmartNarrativeInsights();
|
||||
* ```
|
||||
*
|
||||
* @returns {Promise<ISmartNarratives>}
|
||||
*/
|
||||
getSmartNarrativeInsights(): Promise<ISmartNarratives>;
|
||||
/**
|
||||
* Gets all page level filters within the report.
|
||||
*
|
||||
|
@ -2940,12 +2967,13 @@ declare module "powerbi-client" {
|
|||
export { Report } from "report";
|
||||
export { Dashboard } from "dashboard";
|
||||
export { Tile } from "tile";
|
||||
export { IEmbedConfiguration, IQnaEmbedConfiguration, IVisualEmbedConfiguration, IReportEmbedConfiguration, IDashboardEmbedConfiguration, ITileEmbedConfiguration, IQuickCreateConfiguration, Embed, ILocaleSettings, IEmbedSettings, IQnaSettings, } from "embed";
|
||||
export { IEmbedConfiguration, IQnaEmbedConfiguration, IVisualEmbedConfiguration, IReportEmbedConfiguration, IDashboardEmbedConfiguration, ITileEmbedConfiguration, IQuickCreateConfiguration, IReportCreateConfiguration, Embed, ILocaleSettings, IEmbedSettings, IQnaSettings, } from "embed";
|
||||
export { Page } from "page";
|
||||
export { Qna } from "qna";
|
||||
export { Visual } from "visual";
|
||||
export { VisualDescriptor } from "visualDescriptor";
|
||||
export { QuickCreate } from "quickCreate";
|
||||
export { Create } from "create";
|
||||
export { BasicFilterBuilder, AdvancedFilterBuilder, TopNFilterBuilder, RelativeDateFilterBuilder, RelativeTimeFilterBuilder } from "FilterBuilders/index";
|
||||
global {
|
||||
interface Window {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "powerbi-client",
|
||||
"version": "2.22.2",
|
||||
"version": "2.23.1",
|
||||
"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",
|
||||
|
@ -79,9 +79,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"http-post-message": "^0.2",
|
||||
"powerbi-models": "^1.12.3",
|
||||
"powerbi-models": "^1.14.0",
|
||||
"powerbi-router": "^0.1",
|
||||
"window-post-message-proxy": "^0.2"
|
||||
"window-post-message-proxy": "^0.2.7"
|
||||
},
|
||||
"publishConfig": {
|
||||
"tag": "beta"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
/** @ignore *//** */
|
||||
const config = {
|
||||
version: '2.22.2',
|
||||
version: '2.23.1',
|
||||
type: 'js'
|
||||
};
|
||||
|
||||
|
|
15
src/embed.ts
15
src/embed.ts
|
@ -3,9 +3,9 @@
|
|||
|
||||
import * as models from 'powerbi-models';
|
||||
import * as sdkConfig from './config';
|
||||
import { EmbedUrlNotSupported } from './errors';
|
||||
import { EmbedUrlNotSupported, invalidEmbedUrlErrorMessage } from './errors';
|
||||
import { ICustomEvent, IEvent, IEventHandler, Service } from './service';
|
||||
import { addParamToUrl, assign, autoAuthInEmbedUrl, createRandomString, getTimeDiffInMilliseconds, remove, isCreate } from './util';
|
||||
import { addParamToUrl, assign, autoAuthInEmbedUrl, createRandomString, getTimeDiffInMilliseconds, remove, isCreate, validateEmbedUrl } from './util';
|
||||
|
||||
declare global {
|
||||
interface Document {
|
||||
|
@ -50,6 +50,8 @@ export type IQnaEmbedConfiguration = models.IQnaEmbedConfiguration;
|
|||
|
||||
export type IQuickCreateConfiguration = models.IQuickCreateConfiguration;
|
||||
|
||||
export type IReportCreateConfiguration = models.IReportCreateConfiguration;
|
||||
|
||||
export type ILocaleSettings = models.ILocaleSettings;
|
||||
|
||||
export type IQnaSettings = models.IQnaSettings;
|
||||
|
@ -571,7 +573,7 @@ export abstract class Embed {
|
|||
|
||||
const accessTokenProvider = eventHooks.accessTokenProvider;
|
||||
if (!!accessTokenProvider) {
|
||||
if ((['create', 'quickcreate', 'report'].indexOf(this.embedtype.toLowerCase()) === -1) || this.config.tokenType !== models.TokenType.Aad) {
|
||||
if ((['create', 'quickcreate', 'report'].indexOf(this.embedtype.toLowerCase()) === -1) || this.config.tokenType !== models.TokenType.Aad) {
|
||||
throw new Error("accessTokenProvider is only supported in report SaaS embed");
|
||||
}
|
||||
}
|
||||
|
@ -632,10 +634,6 @@ export abstract class Embed {
|
|||
// Trim spaces to fix user mistakes.
|
||||
hostname = hostname.toLowerCase().trim();
|
||||
|
||||
if (hostname.indexOf("http://") === 0) {
|
||||
throw new Error("HTTP is not allowed. HTTPS is required");
|
||||
}
|
||||
|
||||
if (hostname.indexOf("https://") === 0) {
|
||||
return `${hostname}/${endpoint}`;
|
||||
}
|
||||
|
@ -743,6 +741,9 @@ export abstract class Embed {
|
|||
if (!this.iframe) {
|
||||
const iframeContent = document.createElement("iframe");
|
||||
const embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl;
|
||||
if (!validateEmbedUrl(embedUrl)) {
|
||||
throw new Error(invalidEmbedUrlErrorMessage);
|
||||
}
|
||||
|
||||
iframeContent.style.width = '100%';
|
||||
iframeContent.style.height = '100%';
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
|
||||
export const APINotSupportedForRDLError = "This API is currently not supported for RDL reports";
|
||||
export const EmbedUrlNotSupported = "Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL";
|
||||
export const invalidEmbedUrlErrorMessage: string = "Invalid embed URL detected. Either URL hostname or protocol are invalid. Please use Power BI REST APIs to get the valid URL";
|
||||
|
||||
|
|
23
src/page.ts
23
src/page.ts
|
@ -18,6 +18,7 @@ import {
|
|||
VisualContainerDisplayMode,
|
||||
IPageBackground,
|
||||
IPageWallpaper,
|
||||
ISmartNarratives,
|
||||
} from 'powerbi-models';
|
||||
import { IFilterable } from './ifilterable';
|
||||
import { IReportNode, Report } from './report';
|
||||
|
@ -139,6 +140,28 @@ export class Page implements IPageNode, IFilterable {
|
|||
this.wallpaper = wallpaper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get insights for report page
|
||||
*
|
||||
* ```javascript
|
||||
* page.getSmartNarrativeInsights();
|
||||
* ```
|
||||
*
|
||||
* @returns {Promise<ISmartNarratives>}
|
||||
*/
|
||||
async getSmartNarrativeInsights(): Promise<ISmartNarratives > {
|
||||
if (isRDLEmbed(this.report.config.embedUrl)) {
|
||||
return Promise.reject(APINotSupportedForRDLError);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.report.service.hpm.get<ISmartNarratives>(`/report/pages/${this.name}/smartNarrativeInsights`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow);
|
||||
return response.body;
|
||||
} catch (response) {
|
||||
throw response.body;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all page level filters within the report.
|
||||
*
|
||||
|
|
|
@ -32,6 +32,7 @@ export {
|
|||
IDashboardEmbedConfiguration,
|
||||
ITileEmbedConfiguration,
|
||||
IQuickCreateConfiguration,
|
||||
IReportCreateConfiguration,
|
||||
Embed,
|
||||
ILocaleSettings,
|
||||
IEmbedSettings,
|
||||
|
@ -52,6 +53,9 @@ export {
|
|||
export {
|
||||
QuickCreate
|
||||
} from './quickCreate';
|
||||
export {
|
||||
Create
|
||||
} from './create';
|
||||
export {
|
||||
BasicFilterBuilder,
|
||||
AdvancedFilterBuilder,
|
||||
|
|
|
@ -637,6 +637,10 @@ export class Report extends Embed implements IReportNode, IFilterable {
|
|||
* ```
|
||||
*/
|
||||
async refresh(): Promise<void> {
|
||||
if (isRDLEmbed(this.config.embedUrl)) {
|
||||
return Promise.reject(APINotSupportedForRDLError);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.service.hpm.post<void>('/report/refresh', null, { uid: this.config.uniqueId }, this.iframe.contentWindow);
|
||||
return response.body;
|
||||
|
|
|
@ -29,6 +29,7 @@ import { Visual } from './visual';
|
|||
import * as utils from './util';
|
||||
import { QuickCreate } from './quickCreate';
|
||||
import * as sdkConfig from './config';
|
||||
import { invalidEmbedUrlErrorMessage } from './errors';
|
||||
|
||||
export interface IEvent<T> {
|
||||
type: string;
|
||||
|
@ -667,6 +668,10 @@ export class Service implements IService {
|
|||
* @param {HTMLElement} [element=undefined]
|
||||
*/
|
||||
preload(config: IComponentEmbedConfiguration | IEmbedConfigurationBase, element?: HTMLElement): HTMLIFrameElement {
|
||||
if (!utils.validateEmbedUrl(config.embedUrl)) {
|
||||
throw new Error(invalidEmbedUrlErrorMessage);
|
||||
}
|
||||
|
||||
const iframeContent = document.createElement("iframe");
|
||||
iframeContent.setAttribute("style", "display:none;");
|
||||
iframeContent.setAttribute("src", config.embedUrl);
|
||||
|
|
36
src/util.ts
36
src/util.ts
|
@ -3,6 +3,22 @@
|
|||
|
||||
import { HttpPostMessage } from 'http-post-message';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const allowedPowerBiHostsRegex =
|
||||
new RegExp(/(.+\.powerbi\.com$)|(.+\.fabric\.microsoft\.com$)|(.+\.analysis\.windows-int\.net$)|(.+\.analysis-df\.windows\.net$)/);
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const allowedPowerBiHostsSovRegex = new RegExp(/^app\.powerbi\.cn$|^app(\.mil\.|\.high\.|\.)powerbigov\.us$|^app\.powerbi\.eaglex\.ic\.gov$|^app\.powerbi\.microsoft\.scloud$/);
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const expectedEmbedUrlProtocol: string = "https:";
|
||||
|
||||
/**
|
||||
* Raises a custom event with event data on the specified HTML element.
|
||||
*
|
||||
|
@ -194,7 +210,7 @@ export function autoAuthInEmbedUrl(embedUrl: string): boolean {
|
|||
export function getRandomValue(): number {
|
||||
|
||||
// window.msCrypto for IE
|
||||
const cryptoObj = window.crypto || window.msCrypto;
|
||||
const cryptoObj = window.crypto || (window as any).msCrypto;
|
||||
const randomValueArray = new Uint32Array(1);
|
||||
cryptoObj.getRandomValues(randomValueArray);
|
||||
|
||||
|
@ -223,3 +239,21 @@ export function getTimeDiffInMilliseconds(start: Date, end: Date): number {
|
|||
export function isCreate(embedType: string): boolean {
|
||||
return embedType === 'create' || embedType === 'quickcreate';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the embedUrl has an allowed power BI domain
|
||||
* @hidden
|
||||
*/
|
||||
export function validateEmbedUrl(embedUrl: string): boolean {
|
||||
if (embedUrl) {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(embedUrl.toLowerCase());
|
||||
} catch(e) {
|
||||
// invalid URL
|
||||
return false;
|
||||
}
|
||||
return url.protocol === expectedEmbedUrlProtocol &&
|
||||
(allowedPowerBiHostsRegex.test(url.hostname) || allowedPowerBiHostsSovRegex.test(url.hostname));
|
||||
}
|
||||
}
|
|
@ -10,11 +10,12 @@ import {
|
|||
IExportDataResult,
|
||||
IFilter,
|
||||
ISlicerState,
|
||||
ISmartNarratives,
|
||||
ISortByVisualRequest,
|
||||
IUpdateFiltersRequest,
|
||||
IVisualLayout,
|
||||
VisualContainerDisplayMode,
|
||||
VisualLevelFilters
|
||||
VisualLevelFilters,
|
||||
} from 'powerbi-models';
|
||||
import { IHttpPostMessageResponse } from 'http-post-message';
|
||||
import { IFilterable } from './ifilterable';
|
||||
|
@ -319,4 +320,22 @@ export class VisualDescriptor implements IVisualNode, IFilterable {
|
|||
|
||||
return report.resizeVisual(pageName, visualName, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get insights for single visual
|
||||
*
|
||||
* ```javascript
|
||||
* visual.getSmartNarrativeInsights();
|
||||
* ```
|
||||
*
|
||||
* @returns {Promise<ISmartNarratives>}
|
||||
*/
|
||||
async getSmartNarrativeInsights(): Promise<ISmartNarratives> {
|
||||
try {
|
||||
const response = await this.page.report.service.hpm.get<ISmartNarratives>(`/report/pages/${this.page.name}/visuals/${this.name}/smartNarrativeInsights`, { uid: this.page.report.config.uniqueId }, this.page.report.iframe.contentWindow);
|
||||
return response.body;
|
||||
} catch (response) {
|
||||
throw response.body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,10 @@ describe('SDK-to-HPM', function () {
|
|||
};
|
||||
|
||||
spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled.
|
||||
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
|
||||
|
||||
powerbi = new service.Service(spyHpmFactory, noop, spyRouterFactory, { wpmpName: 'SDK-to-HPM report wpmp' });
|
||||
|
||||
|
||||
sdkSessionId = powerbi.getSdkSessionId();
|
||||
});
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ describe('SDK-to-MockApp', function () {
|
|||
powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory, {
|
||||
wpmpName: 'SDK-to-MockApp HostWpmp'
|
||||
});
|
||||
|
||||
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
|
||||
|
||||
element = document.createElement('div');
|
||||
element.id = "reportContainer1";
|
||||
element.className = 'powerbi-report-container2';
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as service from '../src/service';
|
|||
import * as report from '../src/report';
|
||||
import * as Wpmp from 'window-post-message-proxy';
|
||||
import * as factories from '../src/factories';
|
||||
import * as utils from '../src/util';
|
||||
import { spyWpmp } from './utility/mockWpmp';
|
||||
import { spyHpm } from './utility/mockHpm';
|
||||
import { spyRouter } from './utility/mockRouter';
|
||||
|
@ -17,6 +18,7 @@ describe('SDK-to-WPMP', function () {
|
|||
let uniqueId: string;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
|
||||
const spyWpmpFactory: factories.IWpmpFactory = (_name?: string, _logMessages?: boolean) => {
|
||||
return <Wpmp.WindowPostMessageProxy>spyWpmp;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ import * as embed from '../src/embed';
|
|||
import * as report from '../src/report';
|
||||
import * as create from '../src/create';
|
||||
import * as factories from '../src/factories';
|
||||
import * as utils from '../src/util';
|
||||
import { EmbedUrlNotSupported } from '../src/errors';
|
||||
|
||||
// Todo: remove JQuery usage from this tests file.
|
||||
|
@ -42,6 +43,7 @@ describe('service', function () {
|
|||
let element: HTMLDivElement;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
|
||||
powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory);
|
||||
powerbi.accessToken = 'ABC123';
|
||||
element = document.createElement('div');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import * as service from '../src/service';
|
||||
import * as factories from '../src/factories';
|
||||
import * as utils from '../src/util';
|
||||
|
||||
// Avoid adding new tests to this file, create another spec file instead.
|
||||
|
||||
|
@ -12,6 +13,7 @@ describe('embed', function () {
|
|||
let iframe: HTMLIFrameElement;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
|
||||
powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory);
|
||||
powerbi.accessToken = 'ABC123';
|
||||
container = document.createElement('iframe');
|
||||
|
|
Загрузка…
Ссылка в новой задаче