Skip to content

Commit bd559cd

Browse files
SCAL-239528 updated the embed types to routeBlocking object
1 parent dcca820 commit bd559cd

File tree

7 files changed

+5459
-4822
lines changed

7 files changed

+5459
-4822
lines changed

src/embed/ts-embed.spec.ts

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
CustomActionTarget,
3030
CustomActionsPosition,
3131
DefaultAppInitData,
32+
NavigationPath,
3233
} from '../types';
3334
import {
3435
executeAfterWait,
@@ -3857,7 +3858,9 @@ describe('Unit test case for ts embed', () => {
38573858
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
38583859
...defaultViewConfig,
38593860
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
3860-
allowedRoutes: ['/custom/route'],
3861+
routeBlocking: {
3862+
allowedRoutes: ['/custom/route'],
3863+
},
38613864
});
38623865

38633866
liveboardEmbed.render();
@@ -3881,6 +3884,7 @@ describe('Unit test case for ts embed', () => {
38813884
);
38823885

38833886
expect(appInitData.allowedRoutes).toContain('/custom/route');
3887+
expect(appInitData.allowedRoutes).toContain(NavigationPath.Login);
38843888

38853889
expect(appInitData.blockedRoutes).toEqual([]);
38863890
});
@@ -3895,7 +3899,9 @@ describe('Unit test case for ts embed', () => {
38953899
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
38963900
...defaultViewConfig,
38973901
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
3898-
blockedRoutes: ['/admin', '/settings'],
3902+
routeBlocking: {
3903+
blockedRoutes: ['/admin', '/settings'],
3904+
},
38993905
});
39003906

39013907
liveboardEmbed.render();
@@ -3926,8 +3932,10 @@ describe('Unit test case for ts embed', () => {
39263932
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
39273933
...defaultViewConfig,
39283934
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
3929-
allowedRoutes: ['/home'],
3930-
blockedRoutes: ['/admin'],
3935+
routeBlocking: {
3936+
allowedRoutes: ['/home'],
3937+
blockedRoutes: ['/admin'],
3938+
},
39313939
});
39323940

39333941
jest.spyOn(liveboardEmbed as any, 'handleError').mockImplementation(mockHandleError);
@@ -3949,35 +3957,6 @@ describe('Unit test case for ts embed', () => {
39493957
});
39503958
});
39513959

3952-
test('should return empty arrays when no routes are configured', async () => {
3953-
const mockEmbedEventPayload = {
3954-
type: EmbedEvent.APP_INIT,
3955-
data: {},
3956-
};
3957-
3958-
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
3959-
...defaultViewConfig,
3960-
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
3961-
});
3962-
3963-
liveboardEmbed.render();
3964-
const mockPort: any = {
3965-
postMessage: jest.fn(),
3966-
};
3967-
3968-
await executeAfterWait(() => {
3969-
const iframe = getIFrameEl();
3970-
postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
3971-
});
3972-
3973-
await executeAfterWait(() => {
3974-
const appInitData = mockPort.postMessage.mock.calls[0][0].data;
3975-
3976-
expect(appInitData.allowedRoutes).toEqual([]);
3977-
expect(appInitData.blockedRoutes).toEqual([]);
3978-
});
3979-
});
3980-
39813960
test('should auto-generate routes for AppEmbed with pageId and merge with user allowedRoutes', async () => {
39823961
const mockEmbedEventPayload = {
39833962
type: EmbedEvent.APP_INIT,
@@ -3987,7 +3966,9 @@ describe('Unit test case for ts embed', () => {
39873966
const appEmbed = new AppEmbed(getRootEl(), {
39883967
...defaultViewConfig,
39893968
pageId: 'home' as any,
3990-
allowedRoutes: ['/custom/app/route'],
3969+
routeBlocking: {
3970+
allowedRoutes: ['/custom/app/route'],
3971+
},
39913972
});
39923973

39933974
appEmbed.render();
@@ -4005,6 +3986,7 @@ describe('Unit test case for ts embed', () => {
40053986

40063987
expect(appInitData.allowedRoutes).toContain('/home');
40073988
expect(appInitData.allowedRoutes).toContain('/custom/app/route');
3989+
expect(appInitData.allowedRoutes).toContain(NavigationPath.Login);
40083990
});
40093991
});
40103992

@@ -4020,8 +4002,10 @@ describe('Unit test case for ts embed', () => {
40204002
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
40214003
...defaultViewConfig,
40224004
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
4023-
accessDeniedMessage: customMessage,
4024-
allowedRoutes: ['/dashboard'],
4005+
routeBlocking: {
4006+
accessDeniedMessage: customMessage,
4007+
allowedRoutes: ['/dashboard'],
4008+
},
40254009
});
40264010

40274011
liveboardEmbed.render();
@@ -4040,7 +4024,7 @@ describe('Unit test case for ts embed', () => {
40404024
expect(appInitData.allowedRoutes.length).toBeGreaterThan(0);
40414025
});
40424026
});
4043-
4027+
40444028
test('should return error when blockedRoute conflicts with auto-generated route', async () => {
40454029
const mockHandleError = jest.fn();
40464030
const mockEmbedEventPayload = {
@@ -4051,7 +4035,9 @@ describe('Unit test case for ts embed', () => {
40514035
const appEmbed = new AppEmbed(getRootEl(), {
40524036
...defaultViewConfig,
40534037
pageId: 'home' as any,
4054-
blockedRoutes: ['/home'],
4038+
routeBlocking: {
4039+
blockedRoutes: ['/home'],
4040+
},
40554041
});
40564042

40574043
jest.spyOn(appEmbed as any, 'handleError').mockImplementation(mockHandleError);
@@ -4072,5 +4058,35 @@ describe('Unit test case for ts embed', () => {
40724058
);
40734059
});
40744060
});
4061+
4062+
test('should handle empty routeBlocking object', async () => {
4063+
const mockEmbedEventPayload = {
4064+
type: EmbedEvent.APP_INIT,
4065+
data: {},
4066+
};
4067+
4068+
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
4069+
...defaultViewConfig,
4070+
liveboardId: '33248a57-cc70-4e39-9199-fb5092283381',
4071+
routeBlocking: {},
4072+
});
4073+
4074+
liveboardEmbed.render();
4075+
const mockPort: any = {
4076+
postMessage: jest.fn(),
4077+
};
4078+
4079+
await executeAfterWait(() => {
4080+
const iframe = getIFrameEl();
4081+
postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
4082+
});
4083+
4084+
await executeAfterWait(() => {
4085+
const appInitData = mockPort.postMessage.mock.calls[0][0].data;
4086+
expect(appInitData.allowedRoutes).toEqual([]);
4087+
expect(appInitData.blockedRoutes).toEqual([]);
4088+
expect(appInitData.accessDeniedMessage).toBe('');
4089+
});
4090+
});
40754091
});
40764092
});

src/embed/ts-embed.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ export class TsEmbed {
449449
message: customActionsResult.errors,
450450
});
451451
}
452-
const blockedAndAllowedRoutesResult = getBlockedAndAllowedRoutes( this.viewConfig.blockedRoutes, this.viewConfig.allowedRoutes, { embedComponentType: (this.viewConfig as any).embedComponentType || '', liveboardId: (this.viewConfig as any).liveboardId, vizId: (this.viewConfig as any).vizId, activeTabId: (this.viewConfig as any).activeTabId, pageId: (this.viewConfig as any).pageId, path: (this.viewConfig as any).path });
452+
const blockedAndAllowedRoutesResult = getBlockedAndAllowedRoutes( this.viewConfig?.routeBlocking, { embedComponentType: (this.viewConfig as any).embedComponentType || '', liveboardId: (this.viewConfig as any).liveboardId, vizId: (this.viewConfig as any).vizId, activeTabId: (this.viewConfig as any).activeTabId, pageId: (this.viewConfig as any).pageId, path: (this.viewConfig as any).path });
453453
if(blockedAndAllowedRoutesResult.error) {
454454
this.handleError(blockedAndAllowedRoutesResult.message);
455455
}
@@ -474,7 +474,7 @@ export class TsEmbed {
474474
customActions: customActionsResult.actions,
475475
allowedRoutes: blockedAndAllowedRoutesResult.allowedRoutes,
476476
blockedRoutes: blockedAndAllowedRoutesResult.blockedRoutes,
477-
accessDeniedMessage: this.viewConfig.accessDeniedMessage || '',
477+
accessDeniedMessage: blockedAndAllowedRoutesResult.message || '',
478478
...getInterceptInitData(this.viewConfig),
479479
};
480480

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {
6666
CustomActionTarget,
6767
InterceptedApiType,
6868
NavigationPath,
69+
RouteBlocking,
6970
} from './types';
7071
import { CustomCssVariables } from './css-variables';
7172
import { SageEmbed, SageViewConfig } from './embed/sage';
@@ -156,6 +157,7 @@ export {
156157
CustomActionTarget,
157158
InterceptedApiType,
158159
NavigationPath,
160+
RouteBlocking,
159161
};
160162

161163
export { resetCachedAuthToken } from './authToken';

src/types.ts

Lines changed: 87 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,71 +1099,99 @@ export interface BaseViewConfig extends ApiInterceptFlags {
10991099
customActions?: CustomAction[];
11001100

11011101
/**
1102-
* Array of routes that are allowed to be accessed in the embedded app.
1103-
* When specified, navigation will be restricted to only these routes.
1104-
* Use Path.All to allow all routes without restrictions.
1105-
*
1106-
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1107-
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
1108-
* @example
1102+
* Route blocking configuration for the embedded app.
11091103
*
1110-
* // Replace <EmbedComponent> with embed component name. For example, AppEmbed, SearchEmbed, or LiveboardEmbed
1111-
* ```js
1112-
* const embed = new AppEmbed('#tsEmbed', {
1113-
* ... // other embed view config
1114-
* allowedRoutes: [Path.Home, Path.Search, Path.Liveboards],
1115-
* accessDeniedMessage: 'You do not have access to this page'
1116-
* })```
1117-
*
1118-
* // Allow all routes
1119-
* ```js
1120-
* const embed = new AppEmbed('#tsEmbed', {
1121-
* allowedRoutes: [Path.All]
1122-
* })
1123-
* ```
1124-
*/
1125-
allowedRoutes?: (NavigationPath | string)[];
1126-
1127-
/**
1128-
* Array of routes that are blocked from being accessed in the embedded app.
1129-
* When specified, navigation will be restricted to only these routes.
1130-
* Use Path.All to block all routes without restrictions.
1104+
* Control which routes users can navigate to within the embedded application
1105+
* using either an allowlist (`allowedRoutes`) or a blocklist (`blockedRoutes`).
11311106
*
1132-
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1133-
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
1134-
* @example
1135-
* ```js
1136-
* const embed = new AppEmbed('#tsEmbed', {
1137-
* blockedRoutes: [Path.Home, Path.Search, Path.Liveboards],
1138-
* })
1139-
* ```
1140-
* // Block all routes
1141-
* ```js
1142-
* const embed = new AppEmbed('#tsEmbed', {
1143-
* blockedRoutes: [Path.All]
1144-
* })
1145-
* ```
1146-
*/
1147-
blockedRoutes?: (NavigationPath | string)[];
1148-
/**
1149-
* Custom message to display when a user tries to access a route
1150-
* that is not in the allowedRoutes list.
1107+
* **Important:**
1108+
* - `allowedRoutes` and `blockedRoutes` are mutually exclusive. Use only one at a time.
1109+
* - The path that the user initially embeds is always accessible, regardless of this configuration.
11511110
*
11521111
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1153-
* @default 'Access Denied'
11541112
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
11551113
* @example
1156-
*
11571114
* ```js
11581115
* const embed = new AppEmbed('#tsEmbed', {
1159-
* allowedRoutes: [Path.Home, Path.Liveboards],
1160-
* accessDeniedMessage: 'You do not have permission to access this page.
1161-
* Please contact your administrator.'
1116+
* ... // other embed view config
1117+
* routeBlocking: {
1118+
* allowedRoutes: [Path.Home, Path.Search, Path.Liveboards] || blockedRoutes: [Path.Admin, Path.Settings],
1119+
* accessDeniedMessage: 'You do not have access to this page'
1120+
* }
11621121
* })
11631122
* ```
11641123
*/
1165-
accessDeniedMessage?: string;
1166-
1124+
routeBlocking?: {
1125+
/**
1126+
* Array of routes that are allowed to be accessed in the embedded app.
1127+
* When specified, navigation will be restricted to only these routes.
1128+
* All other routes will be blocked.
1129+
* Use Path.All to allow all routes without restrictions.
1130+
*
1131+
* **Important:** The path that the user initially embeds is always unblocked
1132+
* and accessible, regardless of the
1133+
* `allowedRoutes` or `blockedRoutes` configuration.
1134+
*
1135+
* Note: `allowedRoutes` and `blockedRoutes` are mutually exclusive.
1136+
* Use only one at a time.
1137+
*
1138+
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1139+
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
1140+
* @example
1141+
*
1142+
* // Allow only specific routes
1143+
*```js
1144+
* const embed = new AppEmbed('#tsEmbed', {
1145+
* ... // other embed view config
1146+
* allowedRoutes: [Path.Home, Path.Search, Path.Liveboards],
1147+
* accessDeniedMessage: 'You do not have access to this page'
1148+
* })
1149+
*```
1150+
**/
1151+
allowedRoutes?: (NavigationPath | string)[];
1152+
1153+
/**
1154+
* Array of routes that are blocked from being accessed in the embedded app.
1155+
* When specified, all routes except these will be accessible.
1156+
* Use Path.All to block all routes.
1157+
*
1158+
* **Important:** The path that the user initially embeds is always unblocked
1159+
* and accessible, regardless of the
1160+
* `allowedRoutes` or `blockedRoutes` configuration.
1161+
*
1162+
* Note: `allowedRoutes` and `blockedRoutes` are mutually exclusive.
1163+
* Use only one at a time.
1164+
*
1165+
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1166+
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
1167+
* @example
1168+
*
1169+
* // Block specific routes
1170+
*```js
1171+
* const embed = new AppEmbed('#tsEmbed', {
1172+
* blockedRoutes: [Path.Home, Path.Search, Path.Liveboards],
1173+
* })
1174+
*```
1175+
**/
1176+
blockedRoutes?: (NavigationPath | string)[];
1177+
1178+
/**
1179+
* Custom message to display when a user tries to access a blocked route
1180+
* or a route that is not in the allowedRoutes list.
1181+
*
1182+
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SageEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
1183+
* @default 'Access Denied'
1184+
* @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
1185+
* @example
1186+
*
1187+
*
1188+
* const embed = new AppEmbed('#tsEmbed', {
1189+
* allowedRoutes: [Path.Home, Path.Liveboards],
1190+
* accessDeniedMessage: 'You do not have permission to access this page.'
1191+
* })
1192+
**/
1193+
accessDeniedMessage?: string;
1194+
};
11671195
}
11681196

11691197
/**
@@ -6340,6 +6368,11 @@ export enum NavigationPath {
63406368
AutoAnswer = '/answer/create/auto/:dataSourceId',
63416369
RequestAccessForObject = '/requestaccess/:objectType/:objectId',
63426370
}
6371+
export interface RouteBlocking {
6372+
blockedRoutes?: (NavigationPath | string)[];
6373+
allowedRoutes?: (NavigationPath | string)[];
6374+
accessDeniedMessage?: string;
6375+
}
63436376

63446377
export type ApiInterceptFlags = {
63456378
/**

0 commit comments

Comments
 (0)