Skip to content

Commit 6b29334

Browse files
committed
Merge remote-tracking branch 'powerbi/master' into release_2.23.1
2 parents c4578b6 + 8110830 commit 6b29334

19 files changed

+921
-991
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ package-lock.json
1111
demo/package-lock.json
1212
.vscode
1313
owners.txt
14+
test/util.spec.ts
15+
.config/tsaoptions.json

Diff for: dist/powerbi-client.d.ts

+32-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// powerbi-client v2.22.2
1+
// powerbi-client v2.23.1
22
// Copyright (c) Microsoft Corporation.
33
// Licensed under the MIT License.
44
declare module "config" {
@@ -12,6 +12,7 @@ declare module "config" {
1212
declare module "errors" {
1313
export const APINotSupportedForRDLError = "This API is currently not supported for RDL reports";
1414
export const EmbedUrlNotSupported = "Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL";
15+
export const invalidEmbedUrlErrorMessage: string;
1516
}
1617
declare module "util" {
1718
import { HttpPostMessage } from 'http-post-message';
@@ -124,6 +125,11 @@ declare module "util" {
124125
* @returns {boolean}
125126
*/
126127
export function isCreate(embedType: string): boolean;
128+
/**
129+
* Checks if the embedUrl has an allowed power BI domain
130+
* @hidden
131+
*/
132+
export function validateEmbedUrl(embedUrl: string): boolean;
127133
}
128134
declare module "embed" {
129135
import * as models from 'powerbi-models';
@@ -151,6 +157,7 @@ declare module "embed" {
151157
export type ITileEmbedConfiguration = models.ITileEmbedConfiguration;
152158
export type IQnaEmbedConfiguration = models.IQnaEmbedConfiguration;
153159
export type IQuickCreateConfiguration = models.IQuickCreateConfiguration;
160+
export type IReportCreateConfiguration = models.IReportCreateConfiguration;
154161
export type ILocaleSettings = models.ILocaleSettings;
155162
export type IQnaSettings = models.IQnaSettings;
156163
export type IEmbedSettings = models.ISettings;
@@ -597,7 +604,7 @@ declare module "ifilterable" {
597604
}
598605
}
599606
declare module "visualDescriptor" {
600-
import { ExportDataType, FiltersOperations, ICloneVisualRequest, ICloneVisualResponse, IExportDataResult, IFilter, ISlicerState, ISortByVisualRequest, IVisualLayout, VisualContainerDisplayMode } from 'powerbi-models';
607+
import { ExportDataType, FiltersOperations, ICloneVisualRequest, ICloneVisualResponse, IExportDataResult, IFilter, ISlicerState, ISmartNarratives, ISortByVisualRequest, IVisualLayout, VisualContainerDisplayMode } from 'powerbi-models';
601608
import { IHttpPostMessageResponse } from 'http-post-message';
602609
import { IFilterable } from "ifilterable";
603610
import { IPageNode } from "page";
@@ -794,11 +801,21 @@ declare module "visualDescriptor" {
794801
* @returns {Promise<IHttpPostMessageResponse<void>>}
795802
*/
796803
resizeVisual(width: number, height: number): Promise<IHttpPostMessageResponse<void>>;
804+
/**
805+
* Get insights for single visual
806+
*
807+
* ```javascript
808+
* visual.getSmartNarrativeInsights();
809+
* ```
810+
*
811+
* @returns {Promise<ISmartNarratives>}
812+
*/
813+
getSmartNarrativeInsights(): Promise<ISmartNarratives>;
797814
}
798815
}
799816
declare module "page" {
800817
import { IHttpPostMessageResponse } from 'http-post-message';
801-
import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode, IPageBackground, IPageWallpaper } from 'powerbi-models';
818+
import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode, IPageBackground, IPageWallpaper, ISmartNarratives } from 'powerbi-models';
802819
import { IFilterable } from "ifilterable";
803820
import { IReportNode } from "report";
804821
import { VisualDescriptor } from "visualDescriptor";
@@ -894,6 +911,16 @@ declare module "page" {
894911
* @hidden
895912
*/
896913
constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize, background?: IPageBackground, wallpaper?: IPageWallpaper);
914+
/**
915+
* Get insights for report page
916+
*
917+
* ```javascript
918+
* page.getSmartNarrativeInsights();
919+
* ```
920+
*
921+
* @returns {Promise<ISmartNarratives>}
922+
*/
923+
getSmartNarrativeInsights(): Promise<ISmartNarratives>;
897924
/**
898925
* Gets all page level filters within the report.
899926
*
@@ -2940,12 +2967,13 @@ declare module "powerbi-client" {
29402967
export { Report } from "report";
29412968
export { Dashboard } from "dashboard";
29422969
export { Tile } from "tile";
2943-
export { IEmbedConfiguration, IQnaEmbedConfiguration, IVisualEmbedConfiguration, IReportEmbedConfiguration, IDashboardEmbedConfiguration, ITileEmbedConfiguration, IQuickCreateConfiguration, Embed, ILocaleSettings, IEmbedSettings, IQnaSettings, } from "embed";
2970+
export { IEmbedConfiguration, IQnaEmbedConfiguration, IVisualEmbedConfiguration, IReportEmbedConfiguration, IDashboardEmbedConfiguration, ITileEmbedConfiguration, IQuickCreateConfiguration, IReportCreateConfiguration, Embed, ILocaleSettings, IEmbedSettings, IQnaSettings, } from "embed";
29442971
export { Page } from "page";
29452972
export { Qna } from "qna";
29462973
export { Visual } from "visual";
29472974
export { VisualDescriptor } from "visualDescriptor";
29482975
export { QuickCreate } from "quickCreate";
2976+
export { Create } from "create";
29492977
export { BasicFilterBuilder, AdvancedFilterBuilder, TopNFilterBuilder, RelativeDateFilterBuilder, RelativeTimeFilterBuilder } from "FilterBuilders/index";
29502978
global {
29512979
interface Window {

Diff for: dist/powerbi.js

+770-971
Large diffs are not rendered by default.

Diff for: dist/powerbi.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "powerbi-client",
3-
"version": "2.22.2",
3+
"version": "2.23.1",
44
"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.",
55
"main": "dist/powerbi.js",
66
"types": "dist/powerbi-client.d.ts",
@@ -79,9 +79,9 @@
7979
},
8080
"dependencies": {
8181
"http-post-message": "^0.2",
82-
"powerbi-models": "^1.12.3",
82+
"powerbi-models": "^1.14.0",
8383
"powerbi-router": "^0.1",
84-
"window-post-message-proxy": "^0.2"
84+
"window-post-message-proxy": "^0.2.7"
8585
},
8686
"publishConfig": {
8787
"tag": "beta"

Diff for: src/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
/** @ignore *//** */
55
const config = {
6-
version: '2.22.2',
6+
version: '2.23.1',
77
type: 'js'
88
};
99

Diff for: src/embed.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
import * as models from 'powerbi-models';
55
import * as sdkConfig from './config';
6-
import { EmbedUrlNotSupported } from './errors';
6+
import { EmbedUrlNotSupported, invalidEmbedUrlErrorMessage } from './errors';
77
import { ICustomEvent, IEvent, IEventHandler, Service } from './service';
8-
import { addParamToUrl, assign, autoAuthInEmbedUrl, createRandomString, getTimeDiffInMilliseconds, remove, isCreate } from './util';
8+
import { addParamToUrl, assign, autoAuthInEmbedUrl, createRandomString, getTimeDiffInMilliseconds, remove, isCreate, validateEmbedUrl } from './util';
99

1010
declare global {
1111
interface Document {
@@ -50,6 +50,8 @@ export type IQnaEmbedConfiguration = models.IQnaEmbedConfiguration;
5050

5151
export type IQuickCreateConfiguration = models.IQuickCreateConfiguration;
5252

53+
export type IReportCreateConfiguration = models.IReportCreateConfiguration;
54+
5355
export type ILocaleSettings = models.ILocaleSettings;
5456

5557
export type IQnaSettings = models.IQnaSettings;
@@ -571,7 +573,7 @@ export abstract class Embed {
571573

572574
const accessTokenProvider = eventHooks.accessTokenProvider;
573575
if (!!accessTokenProvider) {
574-
if ((['create', 'quickcreate', 'report'].indexOf(this.embedtype.toLowerCase()) === -1) || this.config.tokenType !== models.TokenType.Aad) {
576+
if ((['create', 'quickcreate', 'report'].indexOf(this.embedtype.toLowerCase()) === -1) || this.config.tokenType !== models.TokenType.Aad) {
575577
throw new Error("accessTokenProvider is only supported in report SaaS embed");
576578
}
577579
}
@@ -632,10 +634,6 @@ export abstract class Embed {
632634
// Trim spaces to fix user mistakes.
633635
hostname = hostname.toLowerCase().trim();
634636

635-
if (hostname.indexOf("http://") === 0) {
636-
throw new Error("HTTP is not allowed. HTTPS is required");
637-
}
638-
639637
if (hostname.indexOf("https://") === 0) {
640638
return `${hostname}/${endpoint}`;
641639
}
@@ -743,6 +741,9 @@ export abstract class Embed {
743741
if (!this.iframe) {
744742
const iframeContent = document.createElement("iframe");
745743
const embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl;
744+
if (!validateEmbedUrl(embedUrl)) {
745+
throw new Error(invalidEmbedUrlErrorMessage);
746+
}
746747

747748
iframeContent.style.width = '100%';
748749
iframeContent.style.height = '100%';

Diff for: src/errors.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33

44
export const APINotSupportedForRDLError = "This API is currently not supported for RDL reports";
55
export const EmbedUrlNotSupported = "Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL";
6+
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";
67

Diff for: src/page.ts

+23
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
VisualContainerDisplayMode,
1919
IPageBackground,
2020
IPageWallpaper,
21+
ISmartNarratives,
2122
} from 'powerbi-models';
2223
import { IFilterable } from './ifilterable';
2324
import { IReportNode, Report } from './report';
@@ -139,6 +140,28 @@ export class Page implements IPageNode, IFilterable {
139140
this.wallpaper = wallpaper;
140141
}
141142

143+
/**
144+
* Get insights for report page
145+
*
146+
* ```javascript
147+
* page.getSmartNarrativeInsights();
148+
* ```
149+
*
150+
* @returns {Promise<ISmartNarratives>}
151+
*/
152+
async getSmartNarrativeInsights(): Promise<ISmartNarratives > {
153+
if (isRDLEmbed(this.report.config.embedUrl)) {
154+
return Promise.reject(APINotSupportedForRDLError);
155+
}
156+
157+
try {
158+
const response = await this.report.service.hpm.get<ISmartNarratives>(`/report/pages/${this.name}/smartNarrativeInsights`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow);
159+
return response.body;
160+
} catch (response) {
161+
throw response.body;
162+
}
163+
}
164+
142165
/**
143166
* Gets all page level filters within the report.
144167
*

Diff for: src/powerbi-client.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export {
3232
IDashboardEmbedConfiguration,
3333
ITileEmbedConfiguration,
3434
IQuickCreateConfiguration,
35+
IReportCreateConfiguration,
3536
Embed,
3637
ILocaleSettings,
3738
IEmbedSettings,
@@ -52,6 +53,9 @@ export {
5253
export {
5354
QuickCreate
5455
} from './quickCreate';
56+
export {
57+
Create
58+
} from './create';
5559
export {
5660
BasicFilterBuilder,
5761
AdvancedFilterBuilder,

Diff for: src/report.ts

+4
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ export class Report extends Embed implements IReportNode, IFilterable {
637637
* ```
638638
*/
639639
async refresh(): Promise<void> {
640+
if (isRDLEmbed(this.config.embedUrl)) {
641+
return Promise.reject(APINotSupportedForRDLError);
642+
}
643+
640644
try {
641645
const response = await this.service.hpm.post<void>('/report/refresh', null, { uid: this.config.uniqueId }, this.iframe.contentWindow);
642646
return response.body;

Diff for: src/service.ts

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { Visual } from './visual';
2929
import * as utils from './util';
3030
import { QuickCreate } from './quickCreate';
3131
import * as sdkConfig from './config';
32+
import { invalidEmbedUrlErrorMessage } from './errors';
3233

3334
export interface IEvent<T> {
3435
type: string;
@@ -667,6 +668,10 @@ export class Service implements IService {
667668
* @param {HTMLElement} [element=undefined]
668669
*/
669670
preload(config: IComponentEmbedConfiguration | IEmbedConfigurationBase, element?: HTMLElement): HTMLIFrameElement {
671+
if (!utils.validateEmbedUrl(config.embedUrl)) {
672+
throw new Error(invalidEmbedUrlErrorMessage);
673+
}
674+
670675
const iframeContent = document.createElement("iframe");
671676
iframeContent.setAttribute("style", "display:none;");
672677
iframeContent.setAttribute("src", config.embedUrl);

Diff for: src/util.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33

44
import { HttpPostMessage } from 'http-post-message';
55

6+
/**
7+
* @hidden
8+
*/
9+
const allowedPowerBiHostsRegex =
10+
new RegExp(/(.+\.powerbi\.com$)|(.+\.fabric\.microsoft\.com$)|(.+\.analysis\.windows-int\.net$)|(.+\.analysis-df\.windows\.net$)/);
11+
12+
/**
13+
* @hidden
14+
*/
15+
const allowedPowerBiHostsSovRegex = new RegExp(/^app\.powerbi\.cn$|^app(\.mil\.|\.high\.|\.)powerbigov\.us$|^app\.powerbi\.eaglex\.ic\.gov$|^app\.powerbi\.microsoft\.scloud$/);
16+
17+
/**
18+
* @hidden
19+
*/
20+
const expectedEmbedUrlProtocol: string = "https:";
21+
622
/**
723
* Raises a custom event with event data on the specified HTML element.
824
*
@@ -194,7 +210,7 @@ export function autoAuthInEmbedUrl(embedUrl: string): boolean {
194210
export function getRandomValue(): number {
195211

196212
// window.msCrypto for IE
197-
const cryptoObj = window.crypto || window.msCrypto;
213+
const cryptoObj = window.crypto || (window as any).msCrypto;
198214
const randomValueArray = new Uint32Array(1);
199215
cryptoObj.getRandomValues(randomValueArray);
200216

@@ -223,3 +239,21 @@ export function getTimeDiffInMilliseconds(start: Date, end: Date): number {
223239
export function isCreate(embedType: string): boolean {
224240
return embedType === 'create' || embedType === 'quickcreate';
225241
}
242+
243+
/**
244+
* Checks if the embedUrl has an allowed power BI domain
245+
* @hidden
246+
*/
247+
export function validateEmbedUrl(embedUrl: string): boolean {
248+
if (embedUrl) {
249+
let url: URL;
250+
try {
251+
url = new URL(embedUrl.toLowerCase());
252+
} catch(e) {
253+
// invalid URL
254+
return false;
255+
}
256+
return url.protocol === expectedEmbedUrlProtocol &&
257+
(allowedPowerBiHostsRegex.test(url.hostname) || allowedPowerBiHostsSovRegex.test(url.hostname));
258+
}
259+
}

Diff for: src/visualDescriptor.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import {
1010
IExportDataResult,
1111
IFilter,
1212
ISlicerState,
13+
ISmartNarratives,
1314
ISortByVisualRequest,
1415
IUpdateFiltersRequest,
1516
IVisualLayout,
1617
VisualContainerDisplayMode,
17-
VisualLevelFilters
18+
VisualLevelFilters,
1819
} from 'powerbi-models';
1920
import { IHttpPostMessageResponse } from 'http-post-message';
2021
import { IFilterable } from './ifilterable';
@@ -319,4 +320,22 @@ export class VisualDescriptor implements IVisualNode, IFilterable {
319320

320321
return report.resizeVisual(pageName, visualName, width, height);
321322
}
323+
324+
/**
325+
* Get insights for single visual
326+
*
327+
* ```javascript
328+
* visual.getSmartNarrativeInsights();
329+
* ```
330+
*
331+
* @returns {Promise<ISmartNarratives>}
332+
*/
333+
async getSmartNarrativeInsights(): Promise<ISmartNarratives> {
334+
try {
335+
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);
336+
return response.body;
337+
} catch (response) {
338+
throw response.body;
339+
}
340+
}
322341
}

Diff for: test/SDK-to-HPM.spec.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ describe('SDK-to-HPM', function () {
4848
};
4949

5050
spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled.
51+
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
5152

5253
powerbi = new service.Service(spyHpmFactory, noop, spyRouterFactory, { wpmpName: 'SDK-to-HPM report wpmp' });
53-
54+
5455
sdkSessionId = powerbi.getSdkSessionId();
5556
});
5657

Diff for: test/SDK-to-MockApp.spec.ts

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ describe('SDK-to-MockApp', function () {
3131
powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory, {
3232
wpmpName: 'SDK-to-MockApp HostWpmp'
3333
});
34+
35+
spyOn(utils, 'validateEmbedUrl').and.callFake(() => { return true; });
36+
3437
element = document.createElement('div');
3538
element.id = "reportContainer1";
3639
element.className = 'powerbi-report-container2';

0 commit comments

Comments
 (0)