Skip to content

Commit 1005e03

Browse files
wenwaMatt Mazzola
authored andcommitted
Add dashboard embed SDK support and tests (microsoft#31)
1 parent 31fa32a commit 1005e03

File tree

5 files changed

+302
-34
lines changed

5 files changed

+302
-34
lines changed

src/dashboard.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as service from './service';
2+
import * as embed from './embed';
3+
import * as models from 'powerbi-models';
4+
import * as wpmp from 'window-post-message-proxy';
5+
import * as hpm from 'http-post-message';
6+
import * as utils from './util';
7+
8+
/**
9+
* A Dashboard node within a dashboard hierarchy
10+
*
11+
* @export
12+
* @interface IDashboardNode
13+
*/
14+
export interface IDashboardNode {
15+
iframe: HTMLIFrameElement;
16+
service: service.IService;
17+
config: embed.IInternalEmbedConfiguration
18+
}
19+
20+
/**
21+
* A Power BI Dashboard embed component
22+
*
23+
* @export
24+
* @class Dashboard
25+
* @extends {embed.Embed}
26+
* @implements {IDashboardNode}
27+
* @implements {IFilterable}
28+
*/
29+
export class Dashboard extends embed.Embed implements IDashboardNode {
30+
static allowedEvents = ["tileClicked", "error"];
31+
static dashboardIdAttribute = 'powerbi-dashboard-id';
32+
static typeAttribute = 'powerbi-type';
33+
static type = "Dashboard";
34+
35+
/**
36+
* Creates an instance of a Power BI Dashboard.
37+
*
38+
* @param {service.Service} service
39+
* @param {HTMLElement} element
40+
*/
41+
constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration) {
42+
super(service, element, config);
43+
Array.prototype.push.apply(this.allowedEvents, Dashboard.allowedEvents);
44+
}
45+
46+
/**
47+
* This adds backwards compatibility for older config which used the dashboardId query param to specify dashboard id.
48+
* E.g. https://powerbi-df.analysis-df.windows.net/dashboardEmbedHost?dashboardId=e9363c62-edb6-4eac-92d3-2199c5ca2a9e
49+
*
50+
* By extracting the id we can ensure id is always explicitly provided as part of the load configuration.
51+
*
52+
* @static
53+
* @param {string} url
54+
* @returns {string}
55+
*/
56+
static findIdFromEmbedUrl(url: string): string {
57+
const dashboardIdRegEx = /dashboardId="?([^&]+)"?/
58+
const dashboardIdMatch = url.match(dashboardIdRegEx);
59+
60+
let dashboardId;
61+
if (dashboardIdMatch) {
62+
dashboardId = dashboardIdMatch[1];
63+
}
64+
65+
return dashboardId;
66+
}
67+
68+
/**
69+
* Get dashboard id from first available location: options, attribute, embed url.
70+
*
71+
* @returns {string}
72+
*/
73+
getId(): string {
74+
const dashboardId = this.config.id || this.element.getAttribute(Dashboard.dashboardIdAttribute) || Dashboard.findIdFromEmbedUrl(this.config.embedUrl);
75+
76+
if (typeof dashboardId !== 'string' || dashboardId.length === 0) {
77+
throw new Error(`Dashboard id is required, but it was not found. You must provide an id either as part of embed configuration or as attribute '${Dashboard.dashboardIdAttribute}'.`);
78+
}
79+
80+
return dashboardId;
81+
}
82+
}

src/embed.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,20 @@ export abstract class Embed {
167167
if (errors) {
168168
throw errors;
169169
}
170-
171-
return this.service.hpm.post<void>('/report/load', config, { uid: this.config.uniqueId }, this.iframe.contentWindow)
172-
.then(response => {
173-
utils.assign(this.config, config);
174-
return response.body;
175-
},
176-
response => {
177-
throw response.body;
178-
});
170+
171+
let loadPath = '/report/load';
172+
if(this.config && this.config.type === 'dashboard') {
173+
loadPath = '/dashboard/load';
174+
}
175+
176+
return this.service.hpm.post<void>(loadPath, config, { uid: this.config.uniqueId }, this.iframe.contentWindow)
177+
.then(response => {
178+
utils.assign(this.config, config);
179+
return response.body;
180+
},
181+
response => {
182+
throw response.body;
183+
});
179184
}
180185

181186
/**

src/service.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as embed from './embed';
22
import { Report } from './report';
3+
import { Dashboard } from './dashboard';
34
import { Tile } from './tile';
45
import { Page } from './page';
56
import * as utils from './util';
@@ -67,9 +68,10 @@ export class Service implements IService {
6768
/**
6869
* A list of components that this service can embed
6970
*/
70-
private static components: (typeof Report | typeof Tile)[] = [
71+
private static components: (typeof Report | typeof Tile | typeof Dashboard)[] = [
7172
Tile,
72-
Report
73+
Report,
74+
Dashboard
7375
];
7476

7577
/**
@@ -90,7 +92,7 @@ export class Service implements IService {
9092
/**The Configuration object for the service*/
9193
private config: IServiceConfiguration;
9294

93-
/** A list of Report and Tile components that have been embedded using this service instance. */
95+
/** A list of Dashboard, Report and Tile components that have been embedded using this service instance. */
9496
private embeds: embed.Embed[];
9597
/** TODO: Look for way to make hpm private without sacraficing ease of maitenance. This should be private but in embed needs to call methods. */
9698
hpm: hpm.HttpPostMessage;
@@ -144,6 +146,16 @@ export class Service implements IService {
144146

145147
this.handleEvent(event);
146148
});
149+
this.router.post(`/dashboards/:uniqueId/events/:eventName`, (req, res) => {
150+
const event: IEvent<any> = {
151+
type: 'dashboard',
152+
id: req.params.uniqueId,
153+
name: req.params.eventName,
154+
value: req.body
155+
};
156+
157+
this.handleEvent(event);
158+
});
147159

148160
this.embeds = [];
149161

@@ -257,7 +269,7 @@ export class Service implements IService {
257269
* @param {HTMLElement} element
258270
* @returns {(Report | Tile)}
259271
*/
260-
get(element: HTMLElement): Report | Tile {
272+
get(element: HTMLElement): Report | Tile | Dashboard {
261273
const powerBiElement = <IPowerBiElement>element;
262274

263275
if (!powerBiElement.powerBiEmbed) {
@@ -273,7 +285,7 @@ export class Service implements IService {
273285
* @param {string} uniqueId
274286
* @returns {(Report | Tile)}
275287
*/
276-
find(uniqueId: string): Report | Tile {
288+
find(uniqueId: string): Report | Tile | Dashboard {
277289
return utils.find(x => x.config.uniqueId === uniqueId, this.embeds);
278290
}
279291

0 commit comments

Comments
 (0)