@thoughtspot/visual-embed-sdk 1.52.1 → 1.52.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +10 -3
- package/cjs/src/contracts/host-event-contracts.d.ts +49 -11
- package/cjs/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/cjs/src/embed/app.d.ts +4 -3
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +10 -0
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/auto-frame-renderer.spec.js +24 -13
- package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +1 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +3 -3
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +35 -24
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/embedConfig.spec.js +2 -1
- package/cjs/src/embed/embedConfig.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +2 -2
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +16 -10
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js +14 -8
- package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.d.ts +9 -0
- package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.js +49 -3
- package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
- package/cjs/src/embed/hostEventClient/utils.spec.js +151 -0
- package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +4 -3
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +17 -12
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +2 -1
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/spotter-utils.d.ts +8 -1
- package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
- package/cjs/src/embed/spotter-utils.js +14 -1
- package/cjs/src/embed/spotter-utils.js.map +1 -1
- package/cjs/src/embed/spotter-utils.spec.js +26 -0
- package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +35 -10
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +108 -73
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +203 -30
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/errors.js +1 -1
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/index.d.ts +2 -1
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +3 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/react/all-types-export.d.ts +1 -1
- package/cjs/src/react/all-types-export.d.ts.map +1 -1
- package/cjs/src/react/all-types-export.js +2 -1
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/types.d.ts +45 -22
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +44 -21
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/authService/tokenizedAuthService.spec.js +2 -1
- package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
- package/cjs/src/utils/custom-actions.spec.js +4 -2
- package/cjs/src/utils/custom-actions.spec.js.map +1 -1
- package/cjs/src/utils/filterConverter.d.ts +106 -0
- package/cjs/src/utils/filterConverter.d.ts.map +1 -0
- package/cjs/src/utils/filterConverter.js +215 -0
- package/cjs/src/utils/filterConverter.js.map +1 -0
- package/cjs/src/utils/filterConverter.spec.d.ts +2 -0
- package/cjs/src/utils/filterConverter.spec.d.ts.map +1 -0
- package/cjs/src/utils/filterConverter.spec.js +709 -0
- package/cjs/src/utils/filterConverter.spec.js.map +1 -0
- package/cjs/src/utils/processData.spec.js +2 -1
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/dist/{index-C1pmYb5H.js → index-BrJMbt8r.js} +1 -1
- package/dist/src/contracts/host-event-contracts.d.ts +49 -11
- package/dist/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +4 -3
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +1 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +2 -2
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/utils.d.ts +9 -0
- package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +4 -3
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/spotter-utils.d.ts +8 -1
- package/dist/src/embed/spotter-utils.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +35 -10
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/react/all-types-export.d.ts +1 -1
- package/dist/src/react/all-types-export.d.ts.map +1 -1
- package/dist/src/types.d.ts +45 -22
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/filterConverter.d.ts +106 -0
- package/dist/src/utils/filterConverter.d.ts.map +1 -0
- package/dist/src/utils/filterConverter.spec.d.ts +2 -0
- package/dist/src/utils/filterConverter.spec.d.ts.map +1 -0
- package/dist/tsembed-react.es.js +252 -126
- package/dist/tsembed-react.js +251 -125
- package/dist/tsembed.es.js +460 -127
- package/dist/tsembed.js +459 -125
- package/dist/visual-embed-sdk-react-full.d.ts +233 -49
- package/dist/visual-embed-sdk-react.d.ts +139 -49
- package/dist/visual-embed-sdk.d.ts +233 -49
- package/lib/package.json +10 -3
- package/lib/src/contracts/host-event-contracts.d.ts +49 -11
- package/lib/src/contracts/host-event-contracts.d.ts.map +1 -1
- package/lib/src/embed/app.d.ts +4 -3
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +10 -0
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/auto-frame-renderer.spec.js +24 -13
- package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +1 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +4 -4
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +35 -24
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/embedConfig.spec.js +2 -1
- package/lib/src/embed/embedConfig.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +2 -2
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +17 -11
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.spec.js +14 -8
- package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.d.ts +9 -0
- package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/utils.js +47 -2
- package/lib/src/embed/hostEventClient/utils.js.map +1 -1
- package/lib/src/embed/hostEventClient/utils.spec.js +152 -1
- package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +4 -3
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +17 -12
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +2 -1
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/spotter-utils.d.ts +8 -1
- package/lib/src/embed/spotter-utils.d.ts.map +1 -1
- package/lib/src/embed/spotter-utils.js +12 -0
- package/lib/src/embed/spotter-utils.js.map +1 -1
- package/lib/src/embed/spotter-utils.spec.js +27 -1
- package/lib/src/embed/spotter-utils.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +35 -10
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +109 -74
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +203 -30
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/errors.js +1 -1
- package/lib/src/errors.js.map +1 -1
- package/lib/src/index.d.ts +2 -1
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -1
- package/lib/src/index.js.map +1 -1
- package/lib/src/react/all-types-export.d.ts +1 -1
- package/lib/src/react/all-types-export.d.ts.map +1 -1
- package/lib/src/react/all-types-export.js +1 -1
- package/lib/src/react/all-types-export.js.map +1 -1
- package/lib/src/types.d.ts +45 -22
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +44 -21
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/authService/tokenizedAuthService.spec.js +2 -1
- package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
- package/lib/src/utils/custom-actions.spec.js +4 -2
- package/lib/src/utils/custom-actions.spec.js.map +1 -1
- package/lib/src/utils/filterConverter.d.ts +106 -0
- package/lib/src/utils/filterConverter.d.ts.map +1 -0
- package/lib/src/utils/filterConverter.js +210 -0
- package/lib/src/utils/filterConverter.js.map +1 -0
- package/lib/src/utils/filterConverter.spec.d.ts +2 -0
- package/lib/src/utils/filterConverter.spec.d.ts.map +1 -0
- package/lib/src/utils/filterConverter.spec.js +707 -0
- package/lib/src/utils/filterConverter.spec.js.map +1 -0
- package/lib/src/utils/processData.spec.js +2 -1
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +233 -49
- package/package.json +10 -3
- package/src/contracts/host-event-contracts.ts +55 -11
- package/src/embed/app.spec.ts +15 -0
- package/src/embed/app.ts +4 -3
- package/src/embed/auto-frame-renderer.spec.ts +24 -13
- package/src/embed/conversation.spec.ts +38 -27
- package/src/embed/conversation.ts +4 -4
- package/src/embed/embedConfig.spec.ts +2 -1
- package/src/embed/hostEventClient/host-event-client.spec.ts +14 -8
- package/src/embed/hostEventClient/host-event-client.ts +18 -10
- package/src/embed/hostEventClient/utils.spec.ts +176 -1
- package/src/embed/hostEventClient/utils.ts +51 -2
- package/src/embed/liveboard.spec.ts +4 -3
- package/src/embed/liveboard.ts +21 -15
- package/src/embed/spotter-utils.spec.ts +29 -0
- package/src/embed/spotter-utils.ts +16 -0
- package/src/embed/ts-embed.spec.ts +245 -30
- package/src/embed/ts-embed.ts +107 -74
- package/src/errors.ts +1 -1
- package/src/index.ts +10 -0
- package/src/react/all-types-export.ts +4 -0
- package/src/types.ts +44 -21
- package/src/utils/authService/tokenizedAuthService.spec.ts +2 -1
- package/src/utils/custom-actions.spec.ts +4 -2
- package/src/utils/filterConverter.spec.ts +801 -0
- package/src/utils/filterConverter.ts +304 -0
- package/src/utils/processData.spec.ts +2 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isValidUpdateFiltersPayload,
|
|
3
|
+
resolveUpdateFiltersAliases,
|
|
3
4
|
isValidUpdateParametersPayload,
|
|
4
5
|
isValidDrillDownPayload,
|
|
5
6
|
createValidationError,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
throwUpdateParametersValidationError,
|
|
8
9
|
throwDrillDownValidationError,
|
|
9
10
|
} from './utils';
|
|
11
|
+
import { HostFilterUpdate as FilterUpdate } from '../../contracts/host-event-contracts';
|
|
10
12
|
import { ERROR_MESSAGE } from '../../errors';
|
|
11
13
|
import { EmbedEvent } from '../../types';
|
|
12
14
|
import { embedEventStatus } from '../../utils';
|
|
@@ -426,4 +428,177 @@ describe('hostEventClient utils', () => {
|
|
|
426
428
|
.toThrow(ERROR_MESSAGE.DRILLDOWN_INVALID_PAYLOAD);
|
|
427
429
|
});
|
|
428
430
|
});
|
|
429
|
-
|
|
431
|
+
// =========================
|
|
432
|
+
// FilterUpdate type shape
|
|
433
|
+
// =========================
|
|
434
|
+
// The @ts-expect-error assertions below are enforced by tsc: an unused one
|
|
435
|
+
// is itself an error, so re-loosening the type fails the build.
|
|
436
|
+
describe('FilterUpdate type', () => {
|
|
437
|
+
it('accepts the documented columnName/operator spelling', () => {
|
|
438
|
+
const filter: FilterUpdate = { columnName: 'region', operator: 'EQ', values: ['west'] };
|
|
439
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('still accepts the deprecated column/oper aliases', () => {
|
|
443
|
+
const filter: FilterUpdate = { column: 'region', oper: 'EQ', values: ['west'] };
|
|
444
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('accepts a mix of documented and deprecated spellings', () => {
|
|
448
|
+
const filter: FilterUpdate = { columnName: 'region', oper: 'EQ', values: ['west'] };
|
|
449
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('accepts a worksheet-qualified column name', () => {
|
|
453
|
+
const filter: FilterUpdate = {
|
|
454
|
+
columnName: '(Sample) Retail - Apparel::city',
|
|
455
|
+
operator: 'IN',
|
|
456
|
+
values: ['atlanta'],
|
|
457
|
+
};
|
|
458
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('rejects a filter with no column at compile time and at runtime', () => {
|
|
462
|
+
// @ts-expect-error one of columnName/column is required
|
|
463
|
+
const filter: FilterUpdate = { operator: 'EQ', values: ['west'] };
|
|
464
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(false);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('rejects a filter with no operator at compile time and at runtime', () => {
|
|
468
|
+
// @ts-expect-error one of operator/oper is required
|
|
469
|
+
const filter: FilterUpdate = { columnName: 'region', values: ['west'] };
|
|
470
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(false);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('rejects both column spellings at compile time', () => {
|
|
474
|
+
// @ts-expect-error columnName and column are mutually exclusive
|
|
475
|
+
const filter: FilterUpdate = {
|
|
476
|
+
columnName: 'region', column: 'region', operator: 'EQ', values: ['west'],
|
|
477
|
+
};
|
|
478
|
+
// The runtime validator is laxer than the type - it reads whichever
|
|
479
|
+
// spelling is present - so this still passes at runtime. The point
|
|
480
|
+
// of the type is to stop the ambiguity reaching it.
|
|
481
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('rejects both operator spellings at compile time', () => {
|
|
485
|
+
// @ts-expect-error operator and oper are mutually exclusive
|
|
486
|
+
const filter: FilterUpdate = {
|
|
487
|
+
columnName: 'region', operator: 'EQ', oper: 'EQ', values: ['west'],
|
|
488
|
+
};
|
|
489
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// The fields SCAL-325540 added to the host-event contract have to keep
|
|
493
|
+
// working alongside the exclusive alias pairs - the union must widen
|
|
494
|
+
// the column and operator halves only, and leave the rest of the
|
|
495
|
+
// filter shape alone.
|
|
496
|
+
it('accepts the fields added by the host-event contract', () => {
|
|
497
|
+
const filter: FilterUpdate = {
|
|
498
|
+
columnName: 'date',
|
|
499
|
+
columnId: 'col-guid',
|
|
500
|
+
operator: 'EQ',
|
|
501
|
+
values: ['2023-07-31'],
|
|
502
|
+
type: 'EXACT_DATE',
|
|
503
|
+
datePeriod: 'DAY',
|
|
504
|
+
negate: true,
|
|
505
|
+
applicability: { level: 'TAB', targetId: 'tab-guid' },
|
|
506
|
+
};
|
|
507
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('accepts non-string values alongside the documented spelling', () => {
|
|
511
|
+
const filter: FilterUpdate = {
|
|
512
|
+
columnName: 'quantity', operator: 'GE', values: [5, true],
|
|
513
|
+
};
|
|
514
|
+
expect(isValidUpdateFiltersPayload({ filter })).toBe(true);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it('accepts the request-level scoping fields', () => {
|
|
518
|
+
const payload = {
|
|
519
|
+
filters: [{ columnName: 'region', operator: 'IN', values: ['west'] }],
|
|
520
|
+
visualizationId: 'viz-guid',
|
|
521
|
+
liveboardId: 'lb-guid',
|
|
522
|
+
};
|
|
523
|
+
expect(isValidUpdateFiltersPayload(payload)).toBe(true);
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
describe('resolveUpdateFiltersAliases', () => {
|
|
527
|
+
it('rewrites columnName/operator to column/oper on a single filter', () => {
|
|
528
|
+
const out = resolveUpdateFiltersAliases({
|
|
529
|
+
filter: { columnName: 'region', operator: 'EQ', values: ['west'] },
|
|
530
|
+
} as any);
|
|
531
|
+
|
|
532
|
+
expect(out.filter).toEqual({ column: 'region', oper: 'EQ', values: ['west'] });
|
|
533
|
+
expect(out.filter).not.toHaveProperty('columnName');
|
|
534
|
+
expect(out.filter).not.toHaveProperty('operator');
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('rewrites every entry in a filters array', () => {
|
|
538
|
+
const out = resolveUpdateFiltersAliases({
|
|
539
|
+
filters: [
|
|
540
|
+
{ columnName: 'region', operator: 'EQ', values: ['west'] },
|
|
541
|
+
{ columnName: 'item type', operator: 'IN', values: ['bags'] },
|
|
542
|
+
],
|
|
543
|
+
} as any);
|
|
544
|
+
|
|
545
|
+
expect(out.filters).toEqual([
|
|
546
|
+
{ column: 'region', oper: 'EQ', values: ['west'] },
|
|
547
|
+
{ column: 'item type', oper: 'IN', values: ['bags'] },
|
|
548
|
+
]);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('leaves a payload that already uses column/oper untouched', () => {
|
|
552
|
+
const payload = { filter: { column: 'region', oper: 'EQ', values: ['west'] } } as any;
|
|
553
|
+
|
|
554
|
+
expect(resolveUpdateFiltersAliases(payload)).toEqual(payload);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('prefers columnName/operator when both spellings are present', () => {
|
|
558
|
+
const out = resolveUpdateFiltersAliases({
|
|
559
|
+
filter: {
|
|
560
|
+
column: 'from-column', columnName: 'from-columnName',
|
|
561
|
+
oper: 'EQ', operator: 'IN',
|
|
562
|
+
values: ['x'],
|
|
563
|
+
},
|
|
564
|
+
} as any);
|
|
565
|
+
|
|
566
|
+
// The value survives under the wire name, but it is the documented
|
|
567
|
+
// spelling's value that survives.
|
|
568
|
+
expect(out.filter).toEqual({ column: 'from-columnName', oper: 'IN', values: ['x'] });
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('preserves the other filter fields, including applicability', () => {
|
|
572
|
+
const out = resolveUpdateFiltersAliases({
|
|
573
|
+
filter: {
|
|
574
|
+
columnName: 'date',
|
|
575
|
+
operator: 'EQ',
|
|
576
|
+
values: [1700000000],
|
|
577
|
+
type: 'EXACT_DATE',
|
|
578
|
+
applicability: { level: 'TAB', targetId: 'tab-guid' },
|
|
579
|
+
},
|
|
580
|
+
} as any);
|
|
581
|
+
|
|
582
|
+
expect(out.filter).toEqual({
|
|
583
|
+
column: 'date',
|
|
584
|
+
oper: 'EQ',
|
|
585
|
+
values: [1700000000],
|
|
586
|
+
type: 'EXACT_DATE',
|
|
587
|
+
applicability: { level: 'TAB', targetId: 'tab-guid' },
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('produces a payload that still passes validation', () => {
|
|
592
|
+
const out = resolveUpdateFiltersAliases({
|
|
593
|
+
filters: [{ columnName: 'region', operator: 'EQ', values: ['west'] }],
|
|
594
|
+
} as any);
|
|
595
|
+
|
|
596
|
+
expect(isValidUpdateFiltersPayload(out as any)).toBe(true);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('is a no-op on a non-object payload', () => {
|
|
600
|
+
expect(resolveUpdateFiltersAliases(undefined as any)).toBeUndefined();
|
|
601
|
+
expect(resolveUpdateFiltersAliases(null as any)).toBeNull();
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
});
|
|
@@ -10,7 +10,8 @@ import { embedEventStatus } from '../../utils';
|
|
|
10
10
|
|
|
11
11
|
const isValidApplicability = (a?: { level?: string; targetId?: string }) => {
|
|
12
12
|
if (isUndefined(a)) return true;
|
|
13
|
-
// targetId is not required at LIVEBOARD level, since the filter applies to
|
|
13
|
+
// targetId is not required at LIVEBOARD level, since the filter applies to
|
|
14
|
+
// the whole Liveboard
|
|
14
15
|
return isPlainObject(a)
|
|
15
16
|
&& Object.values(ApplicabilityLevel).includes(a.level as ApplicabilityLevel)
|
|
16
17
|
&& (a.level === ApplicabilityLevel.Liveboard || (isString(a.targetId) && a.targetId.trim().length > 0));
|
|
@@ -44,8 +45,56 @@ export function isValidUpdateFiltersPayload(
|
|
|
44
45
|
return !!(hasValidFilter || hasValidFilters);
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Rewrites one filter's `columnName`/`operator` to `column`/`oper`.
|
|
50
|
+
*
|
|
51
|
+
* Callers may use either spelling: `columnName`/`operator` match
|
|
52
|
+
* {@link RuntimeFilter} and the `EmbedEvent.FilterChanged` payload, so a
|
|
53
|
+
* converted payload can be replayed as-is. But the payload is forwarded to the
|
|
54
|
+
* embedded app unchanged, and only `column`/`oper` are known to be understood
|
|
55
|
+
* there - so the alias must be swapped here, or it would pass validation and
|
|
56
|
+
* then be silently ignored.
|
|
57
|
+
*
|
|
58
|
+
* If a caller supplies both spellings of a field - which TypeScript rejects,
|
|
59
|
+
* but JavaScript callers can still do - the documented `columnName`/`operator`
|
|
60
|
+
* wins. Deferring to the deprecated spelling would quietly discard the value
|
|
61
|
+
* the rest of the SDK tells people to use.
|
|
62
|
+
* @param filter One entry from the UpdateFilters payload.
|
|
63
|
+
*/
|
|
64
|
+
function resolveFilterAliases<T extends { column?: string; columnName?: string; oper?: string; operator?: string }>(
|
|
65
|
+
filter: T,
|
|
66
|
+
): T {
|
|
67
|
+
if (!isPlainObject(filter)) return filter;
|
|
68
|
+
|
|
69
|
+
const column = filter.columnName ?? filter.column;
|
|
70
|
+
const oper = filter.operator ?? filter.oper;
|
|
71
|
+
const { columnName, operator, ...rest } = filter;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
...rest,
|
|
75
|
+
...(isUndefined(column) ? {} : { column }),
|
|
76
|
+
...(isUndefined(oper) ? {} : { oper }),
|
|
77
|
+
} as T;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Rewrites every filter in an UpdateFilters payload to use `column`/`oper`,
|
|
82
|
+
* whichever spelling the caller supplied. Call after validation.
|
|
83
|
+
* @param payload The UpdateFilters host event payload.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveUpdateFiltersAliases<T extends { filter?: any; filters?: any[] }>(payload: T): T {
|
|
86
|
+
if (!isPlainObject(payload)) return payload;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
...payload,
|
|
90
|
+
...(payload.filter ? { filter: resolveFilterAliases(payload.filter) } : {}),
|
|
91
|
+
...(Array.isArray(payload.filters) ? { filters: payload.filters.map(resolveFilterAliases) } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
47
95
|
export function isValidUpdateParametersPayload(payload: unknown): boolean {
|
|
48
|
-
// Only validates the applicability of each parameter (null treated as
|
|
96
|
+
// Only validates the applicability of each parameter (null treated as
|
|
97
|
+
// absent); the rest is forwarded as-is for backward compatibility.
|
|
49
98
|
if (!Array.isArray(payload)) return true;
|
|
50
99
|
return payload.every((p) => {
|
|
51
100
|
if (!isPlainObject(p)) return true;
|
|
@@ -3054,8 +3054,8 @@ describe('LiveboardEmbed updatedSpotterExperience tests', () => {
|
|
|
3054
3054
|
|
|
3055
3055
|
describe('trigger typing contracts (compile-time)', () => {
|
|
3056
3056
|
// Enforced by ts-jest at compile time. LiveboardEmbed OVERRIDES
|
|
3057
|
-
// TsEmbed.trigger, so its generic defaults must be asserted separately
|
|
3058
|
-
// a bare `PayloadT` on the override silently collapses no-payload
|
|
3057
|
+
// TsEmbed.trigger, so its generic defaults must be asserted separately
|
|
3058
|
+
// — a bare `PayloadT` on the override silently collapses no-payload
|
|
3059
3059
|
// responses back to `any` for every LiveboardEmbed consumer.
|
|
3060
3060
|
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
3061
3061
|
type Extends<A, B> = A extends B ? true : false;
|
|
@@ -3073,7 +3073,8 @@ describe('LiveboardEmbed updatedSpotterExperience tests', () => {
|
|
|
3073
3073
|
{ numberOfTabs: number; orderedTabIds: string[] }
|
|
3074
3074
|
> = true;
|
|
3075
3075
|
const probeUnknownField = (r: TabsResponse) =>
|
|
3076
|
-
// @ts-expect-error — field not on the GetTabs contract (was
|
|
3076
|
+
// @ts-expect-error — field not on the GetTabs contract (was
|
|
3077
|
+
// silent when `any`)
|
|
3077
3078
|
r.tabCount;
|
|
3078
3079
|
void probeUnknownField;
|
|
3079
3080
|
|
package/src/embed/liveboard.ts
CHANGED
|
@@ -37,8 +37,9 @@ import { SpotterChatViewConfig, StarterPromptsConfig } from './conversation';
|
|
|
37
37
|
import { buildStarterPromptsAppInitData } from './spotter-utils';
|
|
38
38
|
import { SpotterVizConfig, buildSpotterVizAppInitData } from './spotter-viz-utils';
|
|
39
39
|
|
|
40
|
-
// Home unmounts the liveboard container, which is how its state gets cleared.
|
|
41
|
-
// settle window gives the container time to do it before we ask for the
|
|
40
|
+
// Home unmounts the liveboard container, which is how its state gets cleared.
|
|
41
|
+
// The settle window gives the container time to do it before we ask for the
|
|
42
|
+
// liveboard back.
|
|
42
43
|
const HOME_ROUTE = 'home';
|
|
43
44
|
const HOME_SETTLE_MS = 200;
|
|
44
45
|
|
|
@@ -354,9 +355,10 @@ export interface LiveboardViewConfig
|
|
|
354
355
|
*/
|
|
355
356
|
isPNGInScheduledEmailsEnabled?: boolean;
|
|
356
357
|
/**
|
|
357
|
-
* Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is
|
|
358
|
-
* following the exact UI layout, instead of splitting
|
|
359
|
-
* This feature is GA from SDK version
|
|
358
|
+
* Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is
|
|
359
|
+
* rendered on a single page following the exact UI layout, instead of splitting
|
|
360
|
+
* visualizations across multiple A4 pages. This feature is GA from SDK version
|
|
361
|
+
* 1.51.0 and ThoughtSpot version 26.8.0.cl.
|
|
360
362
|
*
|
|
361
363
|
* Supported embed types: `AppEmbed`, `LiveboardEmbed`
|
|
362
364
|
* @type {boolean}
|
|
@@ -952,22 +954,25 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
952
954
|
protected beforePrerenderVisible(): void {
|
|
953
955
|
super.beforePrerenderVisible();
|
|
954
956
|
|
|
955
|
-
// Captured before showPreRender() hands the wrapper over to this
|
|
956
|
-
// Itself means a hide/show, not a hand-over, so there is
|
|
957
|
+
// Captured before showPreRender() hands the wrapper over to this
|
|
958
|
+
// instance. Itself means a hide/show, not a hand-over, so there is
|
|
959
|
+
// nothing stale to clear.
|
|
957
960
|
const previous = this.getPreRenderObj<LiveboardEmbed>();
|
|
958
961
|
const showing = previous === this ? undefined : previous?.currentLiveboardState;
|
|
959
962
|
|
|
960
963
|
this.executeAfterEmbedContainerLoaded(async () => {
|
|
961
|
-
// Without this the params callback suspends on its await and
|
|
962
|
-
// goes out first, so the container loads with the previous
|
|
964
|
+
// Without this the params callback suspends on its await and
|
|
965
|
+
// Navigate goes out first, so the container loads with the previous
|
|
966
|
+
// config's filters.
|
|
963
967
|
await this.preRenderParamsApplied;
|
|
964
968
|
if (this.isShowingLiveboardRoute(showing)) {
|
|
965
|
-
// Navigate to the route we are already on is a no-op, so the
|
|
966
|
-
// keeps its state. Home unmounts the container, which
|
|
969
|
+
// Navigate to the route we are already on is a no-op, so the
|
|
970
|
+
// liveboard keeps its state. Home unmounts the container, which
|
|
971
|
+
// clears it.
|
|
967
972
|
this.trigger(HostEvent.Navigate, HOME_ROUTE)
|
|
968
973
|
.catch((error) => logger.warn('Could not route via home', error));
|
|
969
|
-
// Not awaited above: the container never acks Navigate, so
|
|
970
|
-
// only settles on the 30s trigger timeout.
|
|
974
|
+
// Not awaited above: the container never acks Navigate, so
|
|
975
|
+
// that promise only settles on the 30s trigger timeout.
|
|
971
976
|
await new Promise((resolve) => {
|
|
972
977
|
setTimeout(resolve, HOME_SETTLE_MS);
|
|
973
978
|
});
|
|
@@ -987,8 +992,9 @@ export class LiveboardEmbed extends V1Embed {
|
|
|
987
992
|
});
|
|
988
993
|
}
|
|
989
994
|
|
|
990
|
-
// The whole route, not just the liveboard id: the path is built from the
|
|
991
|
-
// and view too, so a same-liveboard show onto another tab is a
|
|
995
|
+
// The whole route, not just the liveboard id: the path is built from the
|
|
996
|
+
// viz, tab and view too, so a same-liveboard show onto another tab is a
|
|
997
|
+
// real navigation.
|
|
992
998
|
private isShowingLiveboardRoute(showing?: LiveboardEmbed['currentLiveboardState']): boolean {
|
|
993
999
|
if (!showing?.liveboardId) return false;
|
|
994
1000
|
const {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
buildSpotterSidebarAppInitData,
|
|
4
4
|
buildSpotterShareConversationAppInitData,
|
|
5
5
|
buildStarterPromptsAppInitData,
|
|
6
|
+
buildSpotterAnalystAppInitData,
|
|
6
7
|
} from './spotter-utils';
|
|
7
8
|
import { ErrorDetailsTypes, EmbedErrorCodes } from '../types';
|
|
8
9
|
import { ERROR_MESSAGE } from '../errors';
|
|
@@ -245,3 +246,31 @@ describe('buildStarterPromptsAppInitData', () => {
|
|
|
245
246
|
expect(result.embedParams?.starterPrompts).toEqual({ enable: true, liveboard });
|
|
246
247
|
});
|
|
247
248
|
});
|
|
249
|
+
|
|
250
|
+
describe('buildSpotterAnalystAppInitData', () => {
|
|
251
|
+
const base = { type: 'APP_INIT' } as any;
|
|
252
|
+
|
|
253
|
+
it('returns the payload unchanged when analystId is absent', () => {
|
|
254
|
+
expect(buildSpotterAnalystAppInitData(base, {})).toBe(base);
|
|
255
|
+
expect(buildSpotterAnalystAppInitData(base, { spotterAnalystConfig: {} })).toBe(base);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('nests spotterAnalystConfig under embedParams', () => {
|
|
259
|
+
const result = buildSpotterAnalystAppInitData(base, {
|
|
260
|
+
spotterAnalystConfig: { analystId: 'analyst-id-1234' },
|
|
261
|
+
});
|
|
262
|
+
expect(result.embedParams?.spotterAnalystConfig).toEqual({ analystId: 'analyst-id-1234' });
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('preserves existing embedParams keys', () => {
|
|
266
|
+
const withExisting = {
|
|
267
|
+
...base,
|
|
268
|
+
embedParams: { spotterSidebarConfig: { enablePastConversationsSidebar: true } },
|
|
269
|
+
} as any;
|
|
270
|
+
const result = buildSpotterAnalystAppInitData(withExisting, {
|
|
271
|
+
spotterAnalystConfig: { analystId: 'analyst-id-1234' },
|
|
272
|
+
});
|
|
273
|
+
expect(result.embedParams?.spotterSidebarConfig).toEqual({ enablePastConversationsSidebar: true });
|
|
274
|
+
expect(result.embedParams?.spotterAnalystConfig).toEqual({ analystId: 'analyst-id-1234' });
|
|
275
|
+
});
|
|
276
|
+
});
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
SpotterShareConversationConfig,
|
|
7
7
|
SpotterChatViewConfig,
|
|
8
8
|
StarterPromptsConfig,
|
|
9
|
+
SpotterAnalystConfig,
|
|
9
10
|
} from './conversation';
|
|
10
11
|
import type { VisualizationOverrides } from '../types';
|
|
11
12
|
|
|
@@ -114,3 +115,18 @@ export function buildStarterPromptsAppInitData<T extends DefaultAppInitData>(
|
|
|
114
115
|
},
|
|
115
116
|
};
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
export function buildSpotterAnalystAppInitData<T extends DefaultAppInitData>(
|
|
120
|
+
initData: T,
|
|
121
|
+
viewConfig: { spotterAnalystConfig?: SpotterAnalystConfig },
|
|
122
|
+
): T & { embedParams?: { spotterAnalystConfig?: SpotterAnalystConfig } } {
|
|
123
|
+
const { spotterAnalystConfig } = viewConfig;
|
|
124
|
+
if (!spotterAnalystConfig?.analystId) return initData;
|
|
125
|
+
return {
|
|
126
|
+
...initData,
|
|
127
|
+
embedParams: {
|
|
128
|
+
...((initData as T & { embedParams?: Record<string, unknown> }).embedParams || {}),
|
|
129
|
+
spotterAnalystConfig,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|