From 57772c1b9beb5a0b39d5e1475bd0b0038271f944 Mon Sep 17 00:00:00 2001 From: Bowei Han Date: Mon, 24 Jan 2022 17:09:02 -0500 Subject: [PATCH] feat: Support multiple queries per subscription (#39) Support multiple queries per subscription --- packages/components/src/components.d.ts | 32 +-- .../iot-bar-chart/iot-bar-chart.spec.ts | 10 +- .../iot-bar-chart/iot-bar-chart.tsx | 4 +- .../iot-connector/iot-connector.spec.ts | 67 +++-- .../iot-connector/iot-connector.tsx | 8 +- .../src/components/iot-kpi/iot-kpi.spec.ts | 10 +- .../src/components/iot-kpi/iot-kpi.tsx | 4 +- .../iot-line-chart/iot-line-chart.spec.ts | 10 +- .../iot-line-chart/iot-line-chart.tsx | 22 +- .../iot-scatter-chart.spec.ts | 10 +- .../iot-scatter-chart/iot-scatter-chart.tsx | 4 +- .../iot-status-grid/iot-status-grid.spec.ts | 10 +- .../iot-status-grid/iot-status-grid.tsx | 4 +- .../iot-status-timeline.spec.ts | 10 +- .../iot-status-timeline.tsx | 4 +- .../components/iot-table/iot-table.spec.ts | 10 +- .../src/components/iot-table/iot-table.tsx | 4 +- .../src/testing/mockWidgetProperties.ts | 2 +- .../components/src/testing/renderChart.tsx | 30 +-- .../testing/testing-ground/siteWiseQueries.ts | 14 +- .../testing/testing-ground/testing-ground.tsx | 79 +++--- .../data-module/IotAppKitDataModule.spec.ts | 233 ++++++++++++++---- .../src/data-module/IotAppKitDataModule.ts | 46 ++-- .../data-source-store/dataSourceStore.ts | 8 + .../subscriptionStore.spec.ts | 16 +- .../subscription-store/subscriptionStore.ts | 6 +- packages/core/src/data-module/types.d.ts | 8 +- .../site-wise/data-source.spec.ts | 20 +- 28 files changed, 453 insertions(+), 232 deletions(-) diff --git a/packages/components/src/components.d.ts b/packages/components/src/components.d.ts index 0710c6b28..587961168 100644 --- a/packages/components/src/components.d.ts +++ b/packages/components/src/components.d.ts @@ -22,21 +22,21 @@ export namespace Components { interface IotBarChart { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; } interface IotConnector { "appKit": DataModule; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "renderFunc": ({ dataStreams }: { dataStreams: DataStream[] }) => unknown; "request": TimeSeriesDataRequest; } interface IotKpi { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; @@ -44,7 +44,7 @@ export namespace Components { interface IotLineChart { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; @@ -68,7 +68,7 @@ export namespace Components { interface IotScatterChart { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; @@ -76,7 +76,7 @@ export namespace Components { interface IotStatusGrid { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; @@ -84,14 +84,14 @@ export namespace Components { interface IotStatusTimeline { "appKit": DataModule; "isEditing": boolean | undefined; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; } interface IotTable { "appKit": DataModule; - "query": AnyDataStreamQuery; + "queries": AnyDataStreamQuery[]; "settings": TimeSeriesDataRequestSettings | undefined; "viewport": MinimalViewPortConfig; "widgetId": string; @@ -268,21 +268,21 @@ declare namespace LocalJSX { interface IotBarChart { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; } interface IotConnector { "appKit"?: DataModule; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "renderFunc"?: ({ dataStreams }: { dataStreams: DataStream[] }) => unknown; "request"?: TimeSeriesDataRequest; } interface IotKpi { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; @@ -290,7 +290,7 @@ declare namespace LocalJSX { interface IotLineChart { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; @@ -314,7 +314,7 @@ declare namespace LocalJSX { interface IotScatterChart { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; @@ -322,7 +322,7 @@ declare namespace LocalJSX { interface IotStatusGrid { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; @@ -330,14 +330,14 @@ declare namespace LocalJSX { interface IotStatusTimeline { "appKit"?: DataModule; "isEditing"?: boolean | undefined; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; } interface IotTable { "appKit"?: DataModule; - "query"?: AnyDataStreamQuery; + "queries"?: AnyDataStreamQuery[]; "settings"?: TimeSeriesDataRequestSettings | undefined; "viewport"?: MinimalViewPortConfig; "widgetId"?: string; diff --git a/packages/components/src/components/iot-bar-chart/iot-bar-chart.spec.ts b/packages/components/src/components/iot-bar-chart/iot-bar-chart.spec.ts index e7e9f5caa..f470774ab 100644 --- a/packages/components/src/components/iot-bar-chart/iot-bar-chart.spec.ts +++ b/packages/components/src/components/iot-bar-chart/iot-bar-chart.spec.ts @@ -27,10 +27,12 @@ const barChartSpecPage = async (propOverrides: Partial = appKit, widgetId: 'test-bar-chart-widget', isEditing: false, - query: { - source: 'test-mock', - assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], - } as SiteWiseDataStreamQuery, + queries: [ + { + source: 'test-mock', + assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], + } as SiteWiseDataStreamQuery, + ], viewport, ...propOverrides, }; diff --git a/packages/components/src/components/iot-bar-chart/iot-bar-chart.tsx b/packages/components/src/components/iot-bar-chart/iot-bar-chart.tsx index 9c7697066..b9e15f2e5 100644 --- a/packages/components/src/components/iot-bar-chart/iot-bar-chart.tsx +++ b/packages/components/src/components/iot-bar-chart/iot-bar-chart.tsx @@ -12,7 +12,7 @@ const DEFAULT_VIEWPORT = { duration: 10 * 1000 * 60 }; export class IotBarChart { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() viewport: MinimalViewPortConfig = DEFAULT_VIEWPORT; @@ -36,7 +36,7 @@ export class IotBarChart { return ( = {}) => { /** Initialize data source and register mock data source */ const appKit = initialize({ registerDataSources: false }); - registerDataSource(appKit, createMockSource([DATA_STREAM])); + registerDataSource(appKit, createMockSource([DATA_STREAM, DATA_STREAM_2])); const page = await newSpecPage({ components: [IotConnector], @@ -27,10 +27,12 @@ const connectorSpecPage = async (propOverrides: Partial const connector = page.doc.createElement('iot-connector') as CustomHTMLElement; const props: Partial = { appKit, - query: { - source: 'test-mock', - assets: [], - } as SiteWiseDataStreamQuery, // static casting because of legacy sw + queries: [ + { + source: 'test-mock', + assets: [], + } as SiteWiseDataStreamQuery, + ], // static casting because of legacy sw request: { viewport, settings: { fetchMostRecentBeforeEnd: true } }, ...propOverrides, }; @@ -52,14 +54,21 @@ it('renders', async () => { it('provides data streams', async () => { const renderFunc = jest.fn(); - const { assetId, propertyId } = toSiteWiseAssetProperty(DATA_STREAM.id); + const { assetId: assetId_1, propertyId: propertyId_1 } = toSiteWiseAssetProperty(DATA_STREAM.id); + const { assetId: assetId_2, propertyId: propertyId_2 } = toSiteWiseAssetProperty(DATA_STREAM_2.id); await connectorSpecPage({ renderFunc, - query: { - source: 'test-mock', - assets: [{ assetId, properties: [{ propertyId }] }], - } as SiteWiseDataStreamQuery, + queries: [ + { + source: 'test-mock', + assets: [{ assetId: assetId_1, properties: [{ propertyId: propertyId_1 }] }], + } as SiteWiseDataStreamQuery, + { + source: 'test-mock', + assets: [{ assetId: assetId_2, properties: [{ propertyId: propertyId_2 }] }], + } as SiteWiseDataStreamQuery, + ], }); await flushPromises(); @@ -69,26 +78,39 @@ it('provides data streams', async () => { expect.objectContaining({ id: DATA_STREAM.id, }), + expect.objectContaining({ + id: DATA_STREAM_2.id, + }), ], }); }); -it('updates with new query', async () => { - const { assetId, propertyId } = toSiteWiseAssetProperty(DATA_STREAM.id); +it('updates with new queries', async () => { + const { assetId: assetId_1, propertyId: propertyId_1 } = toSiteWiseAssetProperty(DATA_STREAM.id); + const { assetId: assetId_2, propertyId: propertyId_2 } = toSiteWiseAssetProperty(DATA_STREAM_2.id); + const renderFunc = jest.fn(); const { connector, page } = await connectorSpecPage({ renderFunc, - query: { - source: 'test-mock', - assets: [], - } as SiteWiseDataStreamQuery, + queries: [ + { + source: 'test-mock', + assets: [], + } as SiteWiseDataStreamQuery, + ], }); await flushPromises(); - connector.query = { - source: 'test-mock', - assets: [{ assetId, properties: [{ propertyId }] }], - } as SiteWiseDataStreamQuery; + connector.queries = [ + { + source: 'test-mock', + assets: [{ assetId: assetId_1, properties: [{ propertyId: propertyId_1 }] }], + } as SiteWiseDataStreamQuery, + { + source: 'test-mock', + assets: [{ assetId: assetId_2, properties: [{ propertyId: propertyId_2 }] }], + } as SiteWiseDataStreamQuery, + ]; await page.waitForChanges(); await flushPromises(); @@ -98,6 +120,9 @@ it('updates with new query', async () => { expect.objectContaining({ id: DATA_STREAM.id, }), + expect.objectContaining({ + id: DATA_STREAM_2.id, + }), ], }); }); diff --git a/packages/components/src/components/iot-connector/iot-connector.tsx b/packages/components/src/components/iot-connector/iot-connector.tsx index 764a2f972..7471132cf 100644 --- a/packages/components/src/components/iot-connector/iot-connector.tsx +++ b/packages/components/src/components/iot-connector/iot-connector.tsx @@ -16,7 +16,7 @@ import { export class IotConnector { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() request: TimeSeriesDataRequest; @@ -33,7 +33,7 @@ export class IotConnector { const { update, unsubscribe } = subscribeToDataStreams( this.appKit, { - query: this.query, + queries: this.queries, request: this.request, }, (dataStreams: DataStream[]) => { @@ -54,11 +54,11 @@ export class IotConnector { * Sync subscription to change in queried data */ @Watch('request') - @Watch('query') + @Watch('queries') onUpdateProp(newProp: unknown, oldProp: unknown) { if (!isEqual(newProp, oldProp) && this.update != null) { this.update({ - query: this.query, + queries: this.queries, request: this.request, }); } diff --git a/packages/components/src/components/iot-kpi/iot-kpi.spec.ts b/packages/components/src/components/iot-kpi/iot-kpi.spec.ts index aee645e60..9a553a717 100644 --- a/packages/components/src/components/iot-kpi/iot-kpi.spec.ts +++ b/packages/components/src/components/iot-kpi/iot-kpi.spec.ts @@ -27,10 +27,12 @@ const kpiSpecPage = async (propOverrides: Partial = {}) => { appKit, widgetId: 'test-kpi-widget', isEditing: false, - query: { - source: 'test-mock', - assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], - } as SiteWiseDataStreamQuery, // static casting because of legacy sw + queries: [ + { + source: 'test-mock', + assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], + } as SiteWiseDataStreamQuery, + ], // static casting because of legacy sw viewport, ...propOverrides, }; diff --git a/packages/components/src/components/iot-kpi/iot-kpi.tsx b/packages/components/src/components/iot-kpi/iot-kpi.tsx index 4440dbcde..d0be610f6 100644 --- a/packages/components/src/components/iot-kpi/iot-kpi.tsx +++ b/packages/components/src/components/iot-kpi/iot-kpi.tsx @@ -12,7 +12,7 @@ const DEFAULT_VIEWPORT = { duration: 10 * 1000 }; export class IotKpi { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() viewport: MinimalViewPortConfig = DEFAULT_VIEWPORT; @@ -36,7 +36,7 @@ export class IotKpi { return ( = {}) appKit, widgetId: 'test-line-chart-widget', isEditing: false, - query: { - source: 'test-mock', - assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], - } as SiteWiseDataStreamQuery, // static casting because of legacy sw + queries: [ + { + source: 'test-mock', + assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], + } as SiteWiseDataStreamQuery, + ], // static casting because of legacy sw viewport, ...propOverrides, }; diff --git a/packages/components/src/components/iot-line-chart/iot-line-chart.tsx b/packages/components/src/components/iot-line-chart/iot-line-chart.tsx index 2b6a5facb..14af6ac78 100644 --- a/packages/components/src/components/iot-line-chart/iot-line-chart.tsx +++ b/packages/components/src/components/iot-line-chart/iot-line-chart.tsx @@ -12,7 +12,7 @@ const DEFAULT_VIEWPORT = { duration: 10 * 1000 * 60 }; export class IotLineChart { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() viewport: MinimalViewPortConfig = DEFAULT_VIEWPORT; @@ -38,19 +38,21 @@ export class IotLineChart { return ( ( - - )} + renderFunc={({ dataStreams }) => { + return ( + + ); + }} /> ); } diff --git a/packages/components/src/components/iot-scatter-chart/iot-scatter-chart.spec.ts b/packages/components/src/components/iot-scatter-chart/iot-scatter-chart.spec.ts index 41f9e13d0..8283117e6 100644 --- a/packages/components/src/components/iot-scatter-chart/iot-scatter-chart.spec.ts +++ b/packages/components/src/components/iot-scatter-chart/iot-scatter-chart.spec.ts @@ -27,10 +27,12 @@ const scatterChartSpecPage = async (propOverrides: Partial = {} appKit, widgetId: 'test-status-grid-widget', isEditing: false, - query: { - source: 'test-mock', - assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], - } as SiteWiseDataStreamQuery, // static casting because of legacy sw + queries: [ + { + source: 'test-mock', + assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], + } as SiteWiseDataStreamQuery, + ], // static casting because of legacy sw viewport, ...propOverrides, }; diff --git a/packages/components/src/components/iot-status-grid/iot-status-grid.tsx b/packages/components/src/components/iot-status-grid/iot-status-grid.tsx index fdf58249a..25bf156e5 100644 --- a/packages/components/src/components/iot-status-grid/iot-status-grid.tsx +++ b/packages/components/src/components/iot-status-grid/iot-status-grid.tsx @@ -12,7 +12,7 @@ const DEFAULT_VIEWPORT = { duration: 10 * 1000 * 60 }; export class IotStatusGrid { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() viewport: MinimalViewPortConfig = DEFAULT_VIEWPORT; @@ -36,7 +36,7 @@ export class IotStatusGrid { return ( = {}) => appKit, widgetId: 'test-table-widget', isEditing: false, - query: { - source: 'test-mock', - assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], - } as SiteWiseDataStreamQuery, // static casting because of legacy sw + queries: [ + { + source: 'test-mock', + assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], + } as SiteWiseDataStreamQuery, + ], // static casting because of legacy sw viewport, ...propOverrides, }; diff --git a/packages/components/src/components/iot-table/iot-table.tsx b/packages/components/src/components/iot-table/iot-table.tsx index 4bd656e75..4ea173dca 100644 --- a/packages/components/src/components/iot-table/iot-table.tsx +++ b/packages/components/src/components/iot-table/iot-table.tsx @@ -12,7 +12,7 @@ const DEFAULT_VIEWPORT = { duration: 10 * 1000 * 60 }; export class IotTable { @Prop() appKit: DataModule; - @Prop() query: AnyDataStreamQuery; + @Prop() queries: AnyDataStreamQuery[]; @Prop() viewport: MinimalViewPortConfig = DEFAULT_VIEWPORT; @@ -34,7 +34,7 @@ export class IotTable { return ( = { }; export const DATA_STREAM_2: DataStream = { - id: 'id-2', + id: toDataStreamId({ assetId: 'some-asset-id-2', propertyId: 'some-property-id-2' }), name: 'data-stream-name-2', color: 'black', resolution: 0, diff --git a/packages/components/src/testing/renderChart.tsx b/packages/components/src/testing/renderChart.tsx index cde458566..6d9ef92cc 100644 --- a/packages/components/src/testing/renderChart.tsx +++ b/packages/components/src/testing/renderChart.tsx @@ -71,7 +71,7 @@ const createMockSiteWiseDataSource = ( resolution: number = 0 ): DataSource => ({ name: SITEWISE_DATA_SOURCE, - initiateRequest: ({ query, request, onSuccess }: DataSourceRequest) => { + initiateRequest: ({ request, onSuccess }: DataSourceRequest) => { const start = (request.viewport as any).start.getTime(); const end = (request.viewport as any).end.getTime(); @@ -115,15 +115,17 @@ defaultAppKit.registerDataSource(dataSource); const defaultChartType = 'iot-line-chart'; -const defaultQuery = { - source: dataSource.name, - assets: [ - { - assetId: 'some-asset-id', - properties: [{ propertyId: 'some-property-id' }], - }, - ], -}; +const defaultQueries = [ + { + source: dataSource.name, + assets: [ + { + assetId: 'some-asset-id', + properties: [{ propertyId: 'some-property-id' }], + }, + ], + }, +]; const defaultSettings = { resolution: DEFAULT_RESOLUTION_MAPPING, fetchAggregatedData: true }; @@ -133,19 +135,19 @@ export const renderChart = ( { chartType = defaultChartType, appKit = defaultAppKit, - query = defaultQuery, + queries = defaultQueries, settings = defaultSettings, viewport = defaultViewport, }: { chartType?: string; appKit?: DataModule; - query?: SiteWiseDataStreamQuery; + queries?: SiteWiseDataStreamQuery[]; settings?: TimeSeriesDataRequestSettings; viewport?: MinimalViewPortConfig; } = { chartType: defaultChartType, appKit: defaultAppKit, - query: defaultQuery, + queries: defaultQueries, settings: defaultSettings, viewport: defaultViewport, } @@ -158,7 +160,7 @@ export const renderChart = ( }, render: function () { const containerProps = { class: testChartContainerClassName, style: { width: '400px', height: '500px' } }; - const chartProps: any = { appKit, query, settings, viewport }; + const chartProps: any = { appKit, queries, settings, viewport }; return (
diff --git a/packages/components/src/testing/testing-ground/siteWiseQueries.ts b/packages/components/src/testing/testing-ground/siteWiseQueries.ts index 968a9b5a8..9cb96204e 100644 --- a/packages/components/src/testing/testing-ground/siteWiseQueries.ts +++ b/packages/components/src/testing/testing-ground/siteWiseQueries.ts @@ -1,12 +1,12 @@ -const STRING_ASSET_ID = 'ab94a0c7-7546-4dc6-9e25-a248f242b362'; -const STRING_PROPERTY_ID = '2b3f10ae-dee5-44a9-9a91-a801ee52c854'; +const STRING_ASSET_ID = 'f2f74fa8-625a-435f-b89c-d27b2d84f45b'; +const STRING_PROPERTY_ID = '797482e4-692f-45a2-b3db-17979481e9c3'; -export const DEMO_TURBINE_ASSET_1 = '25963bcd-cde2-44ef-8e59-7b54da426409'; +export const DEMO_TURBINE_ASSET_1 = 'f2f74fa8-625a-435f-b89c-d27b2d84f45b'; -export const DEMO_TURBINE_ASSET_1_PROPERTY_1 = 'b86ad68c-d102-48df-8ea7-935241112eff'; -export const DEMO_TURBINE_ASSET_1_PROPERTY_2 = 'a8506274-bf65-48dc-a382-1f941e2360db'; -export const DEMO_TURBINE_ASSET_1_PROPERTY_3 = '6d55449b-d0ff-4233-9f02-fc53d6318954'; -export const DEMO_TURBINE_ASSET_1_PROPERTY_4 = '8d9ed440-a8dd-48bd-a35f-70db6f2e860c'; +export const DEMO_TURBINE_ASSET_1_PROPERTY_1 = 'd0dc79be-0dc2-418c-ac23-26f33cdb4b8b'; +export const DEMO_TURBINE_ASSET_1_PROPERTY_2 = '69607dc2-5fbe-416d-aac2-0382018626e4'; +export const DEMO_TURBINE_ASSET_1_PROPERTY_3 = '26072fa0-e36e-489d-90b4-1774e7d12ac9'; +export const DEMO_TURBINE_ASSET_1_PROPERTY_4 = '5c2b7401-57e0-4205-97f2-c4348f12da9a'; export const STRING_QUERY = { source: 'site-wise', diff --git a/packages/components/src/testing/testing-ground/testing-ground.tsx b/packages/components/src/testing/testing-ground/testing-ground.tsx index d76234ba7..17dde4c2d 100755 --- a/packages/components/src/testing/testing-ground/testing-ground.tsx +++ b/packages/components/src/testing/testing-ground/testing-ground.tsx @@ -59,49 +59,64 @@ export class TestingGround {
-
- +
+
@@ -126,7 +141,7 @@ export class TestingGround {
diff --git a/packages/core/src/data-module/IotAppKitDataModule.spec.ts b/packages/core/src/data-module/IotAppKitDataModule.spec.ts index abd3534e5..ad5598468 100644 --- a/packages/core/src/data-module/IotAppKitDataModule.spec.ts +++ b/packages/core/src/data-module/IotAppKitDataModule.spec.ts @@ -1,7 +1,7 @@ import flushPromises from 'flush-promises'; import { DATA_STREAM, DATA_STREAM_INFO, STRING_INFO_1 } from '../testing/__mocks__/mockWidgetProperties'; -import { DataSource, DataSourceRequest } from './types.d'; -import { DataPoint, DataStream, DataStreamInfo, Resolution } from '@synchro-charts/core'; +import { DataSource, DataSourceRequest, DataStreamQuery } from './types.d'; +import { DataPoint, DataStream, DataStreamInfo } from '@synchro-charts/core'; import { TimeSeriesDataRequest, TimeSeriesDataRequestSettings } from './data-cache/requestTypes'; import { DataStreamsStore, DataStreamStore } from './data-cache/types'; import * as caching from './data-cache/caching/caching'; @@ -48,6 +48,22 @@ const createMockSiteWiseDataSource = ( .flat(), }); +const CUSTOM_DATA_SOURCE = 'custom-source'; + +type CustomDataStreamQuery = DataStreamQuery & { + assets: [ + { + id: string; + } + ]; +}; + +const createCustomMockDataSource = (dataStreams: DataStream[]): DataSource => ({ + name: CUSTOM_DATA_SOURCE, + initiateRequest: jest.fn(({ onSuccess }: any) => onSuccess(dataStreams)), + getRequestsFromQuery: ({ query }) => query.assets.map(({ id }) => ({ id, resolution: 0 })), +}); + beforeAll(() => { jest.useFakeTimers('modern'); }); @@ -64,7 +80,7 @@ it('subscribes to an empty set of queries', async () => { const onSuccess = jest.fn(); dataModule.subscribeToDataStreams( { - query: { source: dataSource.name, assets: [] } as SiteWiseDataStreamQuery, + queries: [{ source: dataSource.name, assets: [] } as SiteWiseDataStreamQuery], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2000, 0, 2) }, settings: { @@ -88,11 +104,11 @@ describe('update subscription', () => { const dataStreamCallback = jest.fn(); - const query: SiteWiseDataStreamQuery = { source: dataSource.name, assets: [] }; + const queries: SiteWiseDataStreamQuery[] = [{ source: dataSource.name, assets: [] }]; const { update } = dataModule.subscribeToDataStreams( { - query, + queries, request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -106,7 +122,7 @@ describe('update subscription', () => { dataStreamCallback.mockClear(); (dataSource.initiateRequest as Mock).mockClear(); - update({ query: DATA_STREAM_QUERY }); + update({ queries: [DATA_STREAM_QUERY] }); await flushPromises(); jest.advanceTimersByTime(SECOND_IN_MS); @@ -131,7 +147,7 @@ describe('initial request', () => { dataModule.subscribeToDataStreams( { - query: { source: dataSource.name }, + queries: [{ source: dataSource.name }], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -162,7 +178,7 @@ describe('initial request', () => { dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START, end: END }, settings: { fetchFromStartToEnd: true } }, }, dataStreamCallback @@ -190,15 +206,17 @@ it('subscribes to a single data stream', async () => { const dataStreamCallback = jest.fn(); dataModule.subscribeToDataStreams( { - query: { - source: SITEWISE_DATA_SOURCE, - assets: [ - { - assetId, - properties: [{ propertyId }], - }, - ], - }, + queries: [ + { + source: SITEWISE_DATA_SOURCE, + assets: [ + { + assetId, + properties: [{ propertyId }], + }, + ], + }, + ], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { fetchFromStartToEnd: true }, @@ -222,7 +240,7 @@ it('throws error when subscribing to a non-existent data source', () => { expect(() => dataModule.subscribeToDataStreams( { - query: { source: 'fake-source', assets: [] } as SiteWiseDataStreamQuery, + queries: [{ source: 'fake-source', assets: [] } as SiteWiseDataStreamQuery], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2002, 0, 0) }, settings: { @@ -246,10 +264,12 @@ it('requests data from a custom data source', () => { dataModule.subscribeToDataStreams( { - query: { - assets: [{ assetId, properties: [{ propertyId }] }], - source: customSource.name, - } as SiteWiseDataStreamQuery, + queries: [ + { + assets: [{ assetId, properties: [{ propertyId }] }], + source: customSource.name, + } as SiteWiseDataStreamQuery, + ], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -286,7 +306,7 @@ it('subscribes to multiple data streams', () => { }; dataModule.subscribeToDataStreams( { - query, + queries: [query], request, }, onSuccess @@ -296,6 +316,129 @@ it('subscribes to multiple data streams', () => { expect(onRequestData).toHaveBeenNthCalledWith(2, expect.objectContaining({ dataStreamId: DATA_STREAM_INFO.id })); }); +it('subscribes to multiple queries on the same data source', () => { + const onRequestData = jest.fn(); + const source = createSiteWiseLegacyDataSource(onRequestData); + + const request: TimeSeriesDataRequest = { + viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, + }; + + const dataModule = new IotAppKitDataModule(); + const onSuccess = jest.fn(); + + dataModule.registerDataSource(source); + + const queries = [ + { + source: source.name, + dataStreamInfos: [STRING_INFO_1], + }, + { + source: source.name, + dataStreamInfos: [DATA_STREAM_INFO], + }, + ]; + dataModule.subscribeToDataStreams( + { + queries, + request, + }, + onSuccess + ); + + expect(onRequestData).toHaveBeenNthCalledWith(1, expect.objectContaining({ dataStreamId: STRING_INFO_1.id })); + expect(onRequestData).toHaveBeenNthCalledWith(2, expect.objectContaining({ dataStreamId: DATA_STREAM_INFO.id })); + + expect(onSuccess).toHaveBeenCalledWith([ + expect.objectContaining({ id: STRING_INFO_1.id }), + expect.objectContaining({ id: DATA_STREAM_INFO.id }), + ]); +}); + +it('subscribes to multiple data sources', () => { + const source = createSiteWiseLegacyDataSource(jest.fn()); + const customSource = createCustomMockDataSource([DATA_STREAM]); + + const request: TimeSeriesDataRequest = { + viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, + }; + + const dataModule = new IotAppKitDataModule(); + const onSuccess = jest.fn(); + + dataModule.registerDataSource(source); + dataModule.registerDataSource(customSource); + + const customSourceAssetId = `custom-id`; + + const queries = [ + { + source: source.name, + dataStreamInfos: [STRING_INFO_1], + }, + { + source: customSource.name, + assets: [{ id: customSourceAssetId }], + }, + ]; + dataModule.subscribeToDataStreams( + { + queries, + request, + }, + onSuccess + ); + + expect(onSuccess).toHaveBeenCalledWith([ + expect.objectContaining({ id: STRING_INFO_1.id }), + expect.objectContaining({ id: customSourceAssetId }), + ]); +}); + +it('subscribes to multiple data streams on multiple data sources', () => { + const source = createSiteWiseLegacyDataSource(jest.fn()); + const customSource = createCustomMockDataSource([]); + + const request: TimeSeriesDataRequest = { + viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, + }; + + const dataModule = new IotAppKitDataModule(); + const onSuccess = jest.fn(); + + dataModule.registerDataSource(source); + dataModule.registerDataSource(customSource); + + const customSourceAssetId_1 = `custom-id-1`; + const customSourceAssetId_2 = 'custom-id-2'; + + const queries = [ + { + source: source.name, + dataStreamInfos: [DATA_STREAM_INFO, STRING_INFO_1], + }, + { + source: customSource.name, + assets: [{ id: customSourceAssetId_1 }, { id: customSourceAssetId_2 }], + }, + ]; + dataModule.subscribeToDataStreams( + { + queries, + request, + }, + onSuccess + ); + + expect(onSuccess).toHaveBeenCalledWith([ + expect.objectContaining({ id: DATA_STREAM_INFO.id }), + expect.objectContaining({ id: STRING_INFO_1.id }), + expect.objectContaining({ id: customSourceAssetId_1 }), + expect.objectContaining({ id: customSourceAssetId_2 }), + ]); +}); + it('only requests latest value', () => { const onRequestData = jest.fn(); const source = createSiteWiseLegacyDataSource(onRequestData); @@ -309,10 +452,12 @@ it('only requests latest value', () => { dataModule.subscribeToDataStreams( { - query: { - dataStreamInfos: [DATA_STREAM_INFO], - source: source.name, - } as SiteWiseLegacyDataStreamQuery, + queries: [ + { + dataStreamInfos: [DATA_STREAM_INFO], + source: source.name, + } as SiteWiseLegacyDataStreamQuery, + ], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: LATEST_VALUE_REQUEST_SETTINGS, @@ -373,7 +518,7 @@ describe('error handling', () => { dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: new Date(2000, 0, 0), end: new Date() }, settings: { fetchFromStartToEnd: true }, @@ -396,7 +541,7 @@ describe('error handling', () => { dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { duration: 900000 }, settings: { @@ -424,7 +569,7 @@ describe('error handling', () => { dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: new Date(2000, 0, 0), end: new Date() }, settings: { fetchFromStartToEnd: true }, @@ -453,7 +598,7 @@ describe('caching', () => { const dataStreamCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START_1, end: END_1 }, settings: { fetchFromStartToEnd: true } }, }, dataStreamCallback @@ -485,7 +630,7 @@ describe('caching', () => { const dataStreamCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START_1, end: END_1 }, settings: { fetchFromStartToEnd: true } }, }, dataStreamCallback @@ -526,7 +671,7 @@ describe('caching', () => { const dataStreamCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START_1, end: END_1 }, settings: { fetchFromStartToEnd: true } }, }, dataStreamCallback @@ -559,7 +704,7 @@ describe('caching', () => { const dataStreamCallback = jest.fn(); dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START, end: END }, settings: { fetchFromStartToEnd: true, refreshRate: MINUTE_IN_MS }, @@ -599,9 +744,9 @@ describe('caching', () => { const START = new Date(END.getTime() - HOUR_IN_MS); const dataStreamCallback = jest.fn(); - const { update } = dataModule.subscribeToDataStreams( + dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START, end: END }, settings: { refreshRate: MINUTE_IN_MS } }, }, dataStreamCallback @@ -631,7 +776,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { duration: 900000 }, settings: { fetchFromStartToEnd: true, refreshRate: SECOND_IN_MS * 0.1 }, @@ -669,7 +814,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: START, end: END }, settings: { refreshRate: SECOND_IN_MS * 0.1 }, @@ -707,7 +852,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { duration: SECOND_IN_MS }, settings: { @@ -737,7 +882,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { update, unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -778,7 +923,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { update, unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -816,7 +961,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { duration: SECOND_IN_MS }, settings: { fetchFromStartToEnd: true } }, }, dataStreamCallback @@ -846,7 +991,7 @@ describe('request scheduler', () => { const dataStreamCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { duration: SECOND_IN_MS } }, }, dataStreamCallback @@ -883,7 +1028,7 @@ it('when data is requested from the viewport start to end with a buffer, include const { unsubscribe } = dataModule.subscribeToDataStreams( { - query: DATA_STREAM_QUERY, + queries: [DATA_STREAM_QUERY], request: { viewport: { start, end }, settings: { requestBuffer, fetchFromStartToEnd: true } }, }, dataStreamCallback diff --git a/packages/core/src/data-module/IotAppKitDataModule.ts b/packages/core/src/data-module/IotAppKitDataModule.ts index c0bfc0f0e..9ecd20a81 100644 --- a/packages/core/src/data-module/IotAppKitDataModule.ts +++ b/packages/core/src/data-module/IotAppKitDataModule.ts @@ -70,18 +70,18 @@ export class IotAppKitDataModule implements DataModule { * Takes into account the current state of the cache, to determine which data has already been requested, or has expired * segments within the cache. */ - private fulfillQuery = ({ - query, + private fulfillQueries = ({ + queries, start, end, request, }: { - query: DataStreamQuery; + queries: DataStreamQuery[]; start: Date; end: Date; request: TimeSeriesDataRequest; }) => { - const requestedStreams = this.dataSourceStore.getRequestsFromQuery({ query, request }); + const requestedStreams = this.dataSourceStore.getRequestsFromQueries({ queries, request }); const isRequestedDataStream = ({ id, resolution }: RequestInformation) => this.dataCache.shouldRequestDataStream({ dataStreamId: id, resolution }); @@ -130,25 +130,25 @@ export class IotAppKitDataModule implements DataModule { ); if (requests.length > 0) { - this.registerRequest({ query, request }, requests); + this.registerRequest({ queries, request }, requests); } }; public subscribeToDataStreams = ( - { query, request }: DataModuleSubscription, + { queries, request }: DataModuleSubscription, callback: DataStreamCallback ): SubscriptionResponse => { const subscriptionId = v4(); this.subscriptions.addSubscription(subscriptionId, { - query, + queries, request, emit: callback, fulfill: () => { - this.fulfillQuery({ + this.fulfillQueries({ start: viewportStartDate(request.viewport), end: viewportEndDate(request.viewport), - query, + queries, request, }); }, @@ -177,14 +177,14 @@ export class IotAppKitDataModule implements DataModule { const updatedSubscription = Object.assign({}, subscription, subscriptionUpdate) as Subscription; - if ('query' in updatedSubscription) { + if ('queries' in updatedSubscription) { this.subscriptions.updateSubscription(subscriptionId, { ...updatedSubscription, fulfill: () => { - this.fulfillQuery({ + this.fulfillQueries({ start: viewportStartDate(updatedSubscription.request.viewport), end: viewportEndDate(updatedSubscription.request.viewport), - query: updatedSubscription.query, + queries: updatedSubscription.queries, request: updatedSubscription.request, }); }, @@ -193,17 +193,21 @@ export class IotAppKitDataModule implements DataModule { }; private registerRequest = ( - subscription: { query: Query; request: TimeSeriesDataRequest }, + subscription: { queries: Query[]; request: TimeSeriesDataRequest }, requestInformations: RequestInformationAndRange[] ): void => { - this.dataSourceStore.initiateRequest( - { - request: subscription.request, - query: subscription.query, - onSuccess: this.dataCache.onSuccess(subscription.request), - onError: this.dataCache.onError, - }, - requestInformations + const { queries, request } = subscription; + + queries.forEach((query) => + this.dataSourceStore.initiateRequest( + { + request, + query, + onSuccess: this.dataCache.onSuccess(request), + onError: this.dataCache.onError, + }, + requestInformations + ) ); }; diff --git a/packages/core/src/data-module/data-source-store/dataSourceStore.ts b/packages/core/src/data-module/data-source-store/dataSourceStore.ts index bde59b876..78884f16d 100644 --- a/packages/core/src/data-module/data-source-store/dataSourceStore.ts +++ b/packages/core/src/data-module/data-source-store/dataSourceStore.ts @@ -27,6 +27,14 @@ export default class DataSourceStore { return this.dataSources[source]; }; + public getRequestsFromQueries = ({ + queries, + request, + }: { + queries: Query[]; + request: TimeSeriesDataRequest; + }): RequestInformation[] => queries.map((query) => this.getRequestsFromQuery({ query, request })).flat(); + public getRequestsFromQuery = ({ query, request, diff --git a/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts b/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts index b15502554..afd2fbb37 100644 --- a/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts +++ b/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts @@ -23,7 +23,7 @@ const createSubscriptionStore = () => { const MOCK_SUBSCRIPTION: Subscription = { emit: () => {}, - query: { source: SITEWISE_DATA_SOURCE, assets: [] }, + queries: [{ source: SITEWISE_DATA_SOURCE, assets: [] }], request: { viewport: { start: new Date(2000, 0, 0), end: new Date() }, settings: { @@ -45,15 +45,17 @@ it('updates subscription', () => { const SUBSCRIPTION_ID = 'some-id'; const subscriptionStore = createSubscriptionStore(); - const query = { - source: SITEWISE_DATA_SOURCE, - assets: [{ assetId: '123', properties: [{ propertyId: 'prop1' }, { propertyId: 'prop2' }] }], - }; + const queries = [ + { + source: SITEWISE_DATA_SOURCE, + assets: [{ assetId: '123', properties: [{ propertyId: 'prop1' }, { propertyId: 'prop2' }] }], + }, + ]; subscriptionStore.addSubscription(SUBSCRIPTION_ID, MOCK_SUBSCRIPTION); - subscriptionStore.updateSubscription(SUBSCRIPTION_ID, { query }); + subscriptionStore.updateSubscription(SUBSCRIPTION_ID, { queries }); - expect(subscriptionStore.getSubscriptions()).toEqual([{ ...MOCK_SUBSCRIPTION, query }]); + expect(subscriptionStore.getSubscriptions()).toEqual([{ ...MOCK_SUBSCRIPTION, queries }]); }); it('removes subscription', () => { diff --git a/packages/core/src/data-module/subscription-store/subscriptionStore.ts b/packages/core/src/data-module/subscription-store/subscriptionStore.ts index 219009792..9c1c44b18 100644 --- a/packages/core/src/data-module/subscription-store/subscriptionStore.ts +++ b/packages/core/src/data-module/subscription-store/subscriptionStore.ts @@ -37,7 +37,7 @@ export default class SubscriptionStore { /** * If the subscription is query based */ - if ('query' in subscription) { + if ('queries' in subscription) { subscription.fulfill(); if ('duration' in subscription.request.viewport) { @@ -59,11 +59,11 @@ export default class SubscriptionStore { }); } - const { query, request } = subscription; + const { queries, request } = subscription; // Subscribe to changes from the data cache const unsubscribe = this.dataCache.subscribe( - this.dataSourceStore.getRequestsFromQuery({ query, request }), + this.dataSourceStore.getRequestsFromQueries({ queries, request }), subscription.emit ); diff --git a/packages/core/src/data-module/types.d.ts b/packages/core/src/data-module/types.d.ts index 062412651..003cbae1a 100644 --- a/packages/core/src/data-module/types.d.ts +++ b/packages/core/src/data-module/types.d.ts @@ -35,7 +35,7 @@ export type DataSource = { export type DataStreamCallback = (dataStreams: DataStream[]) => void; export type QuerySubscription = { - query: Query; + queries: Query[]; request: TimeSeriesDataRequest; emit: DataStreamCallback; // Initiate requests for the subscription @@ -46,7 +46,7 @@ export type Subscription = Q export type DataModuleSubscription = { request: TimeSeriesDataRequest; - query: Query; + queries: Query[]; }; export type DataStreamQuery = { @@ -74,7 +74,7 @@ export type DataSourceRequest = { */ export type SubscribeToDataStreams = ( dataModule: DataModule, - { query, requestInfo }: DataModuleSubscription, + { queries, requestInfo }: DataModuleSubscription, callback: DataStreamCallback ) => { unsubscribe: () => void; @@ -82,7 +82,7 @@ export type SubscribeToDataStreams = ( }; type SubscribeToDataStreamsPrivate = ( - { query, requestInfo }: DataModuleSubscription, + { queries, requestInfo }: DataModuleSubscription, callback: DataStreamCallback ) => { unsubscribe: () => void; diff --git a/packages/core/src/data-sources/site-wise/data-source.spec.ts b/packages/core/src/data-sources/site-wise/data-source.spec.ts index 418c27244..9e2b0b9ab 100644 --- a/packages/core/src/data-sources/site-wise/data-source.spec.ts +++ b/packages/core/src/data-sources/site-wise/data-source.spec.ts @@ -275,10 +275,12 @@ describe('e2e through data-module', () => { dataModule.subscribeToDataStreams( { - query: { - assets: [{ assetId, properties: [{ propertyId }] }], - source: dataSource.name, - } as SiteWiseDataStreamQuery, + queries: [ + { + assets: [{ assetId, properties: [{ propertyId }] }], + source: dataSource.name, + } as SiteWiseDataStreamQuery, + ], request: HISTORICAL_REQUEST, }, dataStreamCallback @@ -316,10 +318,12 @@ describe('e2e through data-module', () => { dataModule.subscribeToDataStreams( { - query: { - assets: [{ assetId, properties: [{ propertyId }] }], - source: dataSource.name, - } as SiteWiseDataStreamQuery, + queries: [ + { + assets: [{ assetId, properties: [{ propertyId }] }], + source: dataSource.name, + } as SiteWiseDataStreamQuery, + ], request: { viewport: { start: new Date(2000, 0, 0), end: new Date() }, settings: { fetchMostRecentBeforeEnd: true },