From 7f1226784158bdfff6ab67ab5b0523649a2f7a34 Mon Sep 17 00:00:00 2001 From: Brian Diehr Date: Tue, 30 Aug 2022 15:49:57 -0700 Subject: [PATCH] feat(core)!: Refactor time series data module to remove unused functionality. Add meta field to data stream. BREAKING CHANGE: Refactored export from `@iot-app-kit/core` IoTAppKitDataModule to be named TimeSeriesDataModule, and removed the concept of multiple data sources per time series data module --- packages/components/package.json | 2 +- packages/core/src/__mocks__/data-source.ts | 1 - ...e.spec.ts => TimeSeriesDataModule.spec.ts} | 272 +++--------------- ...tDataModule.ts => TimeSeriesDataModule.ts} | 24 +- .../data-cache/caching/caching.spec.ts | 2 +- .../data-source-store/dataSourceStore.spec.ts | 30 +- .../data-source-store/dataSourceStore.ts | 49 +--- .../subscriptionStore.spec.ts | 13 +- .../subscription-store/subscriptionStore.ts | 13 +- packages/core/src/data-module/types.ts | 15 +- packages/core/src/index.ts | 2 +- .../src/component-session.ts | 7 +- packages/source-iotsitewise/src/initialize.ts | 18 +- packages/source-iotsitewise/src/sessions.ts | 7 +- .../src/time-series-data/data-source.spec.ts | 42 +-- .../src/time-series-data/data-source.ts | 3 - .../src/time-series-data/provider.spec.ts | 8 +- .../subscribeToTimeSeriesData.spec.ts | 9 +- .../subscribeToTimeSeriesData.ts | 4 +- yarn.lock | 81 +++++- 20 files changed, 168 insertions(+), 434 deletions(-) rename packages/core/src/data-module/{IotAppKitDataModule.spec.ts => TimeSeriesDataModule.spec.ts} (80%) rename packages/core/src/data-module/{IotAppKitDataModule.ts => TimeSeriesDataModule.ts} (90%) diff --git a/packages/components/package.json b/packages/components/package.json index cc31c19c8..3a7a0947d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -54,7 +54,7 @@ "@iot-app-kit/related-table": "^1.5.0", "@iot-app-kit/source-iotsitewise": "^1.5.0", "@stencil/core": "^2.7.0", - "@synchro-charts/core": "^6.0.0", + "@synchro-charts/core": "^6.0.1", "styled-components": "^5.3.0" }, "devDependencies": { diff --git a/packages/core/src/__mocks__/data-source.ts b/packages/core/src/__mocks__/data-source.ts index 356132a9f..2225cb174 100644 --- a/packages/core/src/__mocks__/data-source.ts +++ b/packages/core/src/__mocks__/data-source.ts @@ -17,7 +17,6 @@ export const createMockSiteWiseDataSource = ( onRequestData?: (props: any) => void; } = { dataStreams: [], onRequestData: () => {} } ): DataSource => ({ - name: 'site-wise', initiateRequest: jest.fn( ( { query, request, onSuccess = () => {} }: DataSourceRequest, diff --git a/packages/core/src/data-module/IotAppKitDataModule.spec.ts b/packages/core/src/data-module/TimeSeriesDataModule.spec.ts similarity index 80% rename from packages/core/src/data-module/IotAppKitDataModule.spec.ts rename to packages/core/src/data-module/TimeSeriesDataModule.spec.ts index c02a7b23a..5543ee86a 100644 --- a/packages/core/src/data-module/IotAppKitDataModule.spec.ts +++ b/packages/core/src/data-module/TimeSeriesDataModule.spec.ts @@ -1,13 +1,13 @@ import flushPromises from 'flush-promises'; -import { DATA_STREAM, DATA_STREAM_INFO, DATA_STREAM_2 } from '../mockWidgetProperties'; +import { DATA_STREAM, DATA_STREAM_INFO } from '../mockWidgetProperties'; import { createMockSiteWiseDataSource } from '../__mocks__'; -import { DataSource, DataStreamQuery, DataStream, SiteWiseDataStreamQuery } from './types'; +import { DataSource, SiteWiseDataStreamQuery } from './types'; import { DataPoint } 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'; import { HOUR_IN_MS, MINUTE_IN_MS, MONTH_IN_MS, SECOND_IN_MS } from '../common/time'; -import { IotAppKitDataModule } from './IotAppKitDataModule'; +import { TimeSeriesDataModule } from './TimeSeriesDataModule'; import { toSiteWiseAssetProperty, toDataStreamId } from '../common/dataStreamId'; import Mock = jest.Mock; @@ -17,7 +17,6 @@ const { EMPTY_CACHE } = caching; const { propertyId: PROPERTY_ID, assetId: ASSET_ID } = toSiteWiseAssetProperty(DATA_STREAM.id); const DATA_STREAM_QUERY: SiteWiseDataStreamQuery = { - source: 'site-wise', assets: [ { assetId: ASSET_ID, @@ -26,22 +25,6 @@ const DATA_STREAM_QUERY: SiteWiseDataStreamQuery = { ], }; -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'); }); @@ -51,14 +34,13 @@ afterAll(() => { }); it('subscribes to an empty set of queries', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const onSuccess = jest.fn(); dataModule.subscribeToDataStreams( { - queries: [{ source: dataSource.name, assets: [] } as SiteWiseDataStreamQuery], + queries: [{ assets: [] } as SiteWiseDataStreamQuery], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2000, 0, 2) }, settings: { @@ -75,14 +57,12 @@ it('subscribes to an empty set of queries', async () => { describe('update subscription', () => { it('provides new data streams when subscription is updated', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); - const queries: SiteWiseDataStreamQuery[] = [{ source: dataSource.name, assets: [] }]; + const queries: SiteWiseDataStreamQuery[] = [{ assets: [] }]; const { update } = dataModule.subscribeToDataStreams( { @@ -112,20 +92,17 @@ describe('update subscription', () => { describe('initial request', () => { it('does not load request data streams which are not provided from a data-source', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource: DataSource = { - name: 'some-data-source', initiateRequest: jest.fn(), getRequestsFromQuery: () => [], - }; - - dataModule.registerDataSource(dataSource); + } as DataSource; + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); dataModule.subscribeToDataStreams( { - queries: [{ source: dataSource.name }], + queries: [{ source: 'some-source' }], request: { viewport: { start: new Date(2000, 0, 0), end: new Date(2001, 0, 0) }, settings: { @@ -143,7 +120,6 @@ describe('initial request', () => { it('passes back associated refId', () => { const REF_ID = 'ref-id'; const query: SiteWiseDataStreamQuery = { - source: 'site-wise', assets: [ { assetId: ASSET_ID, @@ -155,13 +131,11 @@ describe('initial request', () => { const START = new Date(2000, 0, 0); const END = new Date(); - const dataModule = new IotAppKitDataModule(); const dataSource: DataSource = { ...createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }), initiateRequest: jest.fn(), }; - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); @@ -190,13 +164,11 @@ describe('initial request', () => { const START = new Date(2000, 0, 0); const END = new Date(); - const dataModule = new IotAppKitDataModule(); const dataSource: DataSource = { ...createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }), initiateRequest: jest.fn(), }; - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); @@ -239,9 +211,9 @@ describe('initial request', () => { }); it('subscribes to a single data stream', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); + const { propertyId, assetId } = toSiteWiseAssetProperty(DATA_STREAM.id); const START = new Date(2000, 0, 0); @@ -252,7 +224,6 @@ it('subscribes to a single data stream', async () => { { queries: [ { - source: 'site-wise', assets: [ { assetId, @@ -285,33 +256,13 @@ it('subscribes to a single data stream', async () => { }); }); -it('throws error when subscribing to a non-existent data source', () => { - const dataModule = new IotAppKitDataModule(); - expect(() => - dataModule.subscribeToDataStreams( - { - queries: [{ source: 'fake-source', assets: [] } as SiteWiseDataStreamQuery], - request: { - viewport: { start: new Date(2000, 0, 0), end: new Date(2002, 0, 0) }, - settings: { - fetchFromStartToEnd: true, - }, - }, - }, - () => {} - ) - ).toThrowError(/fake-source/); -}); - it('requests data from a custom data source', () => { const customSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); const { propertyId, assetId } = toSiteWiseAssetProperty(DATA_STREAM.id); - const dataModule = new IotAppKitDataModule(); + const dataModule = new TimeSeriesDataModule(customSource); const onSuccess = jest.fn(); - dataModule.registerDataSource(customSource); - const START = new Date(2000, 0, 0); const END = new Date(2001, 0, 0); @@ -320,7 +271,6 @@ it('requests data from a custom data source', () => { queries: [ { assets: [{ assetId, properties: [{ propertyId }] }], - source: customSource.name, } as SiteWiseDataStreamQuery, ], request: { @@ -360,18 +310,12 @@ it('subscribes to multiple data streams', () => { }, ]; - const dataModule = new IotAppKitDataModule(); + const dataModule = new TimeSeriesDataModule(source); const onSuccess = jest.fn(); - dataModule.registerDataSource(source); - - const query = { - source: source.name, - assets, - }; dataModule.subscribeToDataStreams( { - queries: [query], + queries: [{ assets }], request, }, onSuccess @@ -405,14 +349,11 @@ it('subscribes to multiple queries on the same data source', () => { settings: { fetchFromStartToEnd: true }, }; - const dataModule = new IotAppKitDataModule(); + const dataModule = new TimeSeriesDataModule(source); const onSuccess = jest.fn(); - dataModule.registerDataSource(source); - const queries = [ { - source: source.name, assets: [ { assetId: 'asset-1', @@ -421,7 +362,6 @@ it('subscribes to multiple queries on the same data source', () => { ], }, { - source: source.name, assets: [ { assetId: 'asset-2', @@ -475,138 +415,19 @@ it('subscribes to multiple queries on the same data source', () => { }); }); -it.skip('subscribes to multiple data sources', () => { - const source = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM_2] }); - const customSource = createCustomMockDataSource([DATA_STREAM]); - - const START = new Date(2000, 0, 0); - const END = new Date(2001, 0, 0); - - const request: TimeSeriesDataRequest = { - viewport: { start: START, end: END }, - }; - - const dataModule = new IotAppKitDataModule(); - const onSuccess = jest.fn(); - - dataModule.registerDataSource(source); - dataModule.registerDataSource(customSource); - - const customSourceAssetId = `custom-id`; - - const queries = [ - { - source: source.name, - assets: [ - { - assetId: 'some-asset-id-2', - properties: [{ propertyId: 'some-property-id-2' }], - }, - ], - }, - { - source: customSource.name, - assets: [{ id: customSourceAssetId }], - }, - ]; - dataModule.subscribeToDataStreams( - { - queries, - request, - }, - onSuccess - ); - - expect(onSuccess).toBeCalledWith({ - dataStreams: [ - expect.objectContaining({ id: DATA_STREAM_2.id }), - expect.objectContaining({ id: customSourceAssetId }), - ], - viewport: { - start: START, - end: END, - }, - }); -}); - -it('subscribes to multiple data streams on multiple data sources', () => { - const source = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM_2, DATA_STREAM] }); - const customSource = createCustomMockDataSource([]); - - const START = new Date(2000, 0, 0); - const END = new Date(2001, 0, 0); - - const request: TimeSeriesDataRequest = { - viewport: { start: START, end: END }, - settings: { fetchFromStartToEnd: true }, - }; - - 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, - assets: [ - { - assetId: 'some-asset-id-2', - properties: [{ propertyId: 'some-property-id-2' }], - }, - { - assetId: 'some-asset-id', - properties: [{ propertyId: 'some-property-id' }], - }, - ], - }, - { - source: customSource.name, - assets: [{ id: customSourceAssetId_1 }, { id: customSourceAssetId_2 }], - }, - ]; - dataModule.subscribeToDataStreams( - { - queries, - request, - }, - onSuccess - ); - - expect(onSuccess).toBeCalledWith({ - dataStreams: [ - expect.objectContaining({ id: DATA_STREAM_2.id }), - expect.objectContaining({ id: DATA_STREAM.id }), - expect.objectContaining({ id: customSourceAssetId_1 }), - expect.objectContaining({ id: customSourceAssetId_2 }), - ], - viewport: { - start: START, - end: END, - }, - }); -}); - it('only requests latest value', () => { const onRequestData = jest.fn(); const source = createMockSiteWiseDataSource({ onRequestData }); const LATEST_VALUE_REQUEST_SETTINGS: TimeSeriesDataRequestSettings = { fetchMostRecentBeforeEnd: true }; - const dataModule = new IotAppKitDataModule(); + const dataModule = new TimeSeriesDataModule(source); const onSuccess = jest.fn(); - dataModule.registerDataSource(source); - dataModule.subscribeToDataStreams( { queries: [ { - source: source.name, assets: [ { assetId: 'asset-1', @@ -668,11 +489,9 @@ describe('error handling', () => { it('provides a data stream which has an error associated with it on initial subscription', () => { const customSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - const dataModule = new IotAppKitDataModule({ initialDataCache: CACHE_WITH_ERROR }); + const dataModule = new TimeSeriesDataModule(customSource, { initialDataCache: CACHE_WITH_ERROR }); const timeSeriesCallback = jest.fn(); - dataModule.registerDataSource(customSource); - const START = new Date(2000, 0, 0); const END = new Date(); @@ -700,11 +519,9 @@ describe('error handling', () => { it('does not re-request a data stream with an error associated with it', async () => { const customSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - const dataModule = new IotAppKitDataModule({ initialDataCache: CACHE_WITH_ERROR }); + const dataModule = new TimeSeriesDataModule(customSource, { initialDataCache: CACHE_WITH_ERROR }); const timeSeriesCallback = jest.fn(); - dataModule.registerDataSource(customSource); - dataModule.subscribeToDataStreams( { queries: [DATA_STREAM_QUERY], @@ -727,12 +544,10 @@ describe('error handling', () => { it('does request a data stream which has no error associated with it', () => { const customSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - const dataModule = new IotAppKitDataModule({ initialDataCache: CACHE_WITHOUT_ERROR }); + const dataModule = new TimeSeriesDataModule(customSource, { initialDataCache: CACHE_WITHOUT_ERROR }); const timeSeriesCallback = jest.fn(); - dataModule.registerDataSource(customSource); - const START = new Date(2000, 0, 0); const END = new Date(); @@ -760,9 +575,8 @@ describe('error handling', () => { describe('caching', () => { it('does not request already cached data', () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const START_1 = new Date(2000, 1, 0); const END_1 = new Date(2000, 2, 0); @@ -788,9 +602,8 @@ describe('caching', () => { }); it('requests only uncached data', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); // Order of points through time: // [START_2 ---- [START_1 ----- END_1] ------ END_2] -----> @@ -859,9 +672,8 @@ describe('caching', () => { }); it('immediately request when subscribed to an entirely new time interval not previously requested', () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const START_1 = new Date(2000, 1, 0); const END_1 = new Date(2000, 2, 0); @@ -903,9 +715,8 @@ describe('caching', () => { }); it('requests already cached data if the default TTL has expired', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const END = new Date(); const START = new Date(END.getTime() - HOUR_IN_MS); @@ -955,9 +766,8 @@ describe('caching', () => { }, }; - const dataModule = new IotAppKitDataModule({ cacheSettings: customCacheSettings }); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource, { cacheSettings: customCacheSettings }); const END = new Date(); const START = new Date(END.getTime() - HOUR_IN_MS); @@ -1003,9 +813,8 @@ it.skip('overrides module-level cache TTL if query-level cache TTL is provided', }, }; - const dataModule = new IotAppKitDataModule({ cacheSettings: customCacheSettings }); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource, { cacheSettings: customCacheSettings }); const END = new Date(); const START = new Date(END.getTime() - HOUR_IN_MS); @@ -1062,9 +871,8 @@ it.skip('overrides module-level cache TTL if query-level cache TTL is provided', describe('request scheduler', () => { it('periodically requests duration based queries', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { unsubscribe } = dataModule.subscribeToDataStreams( @@ -1097,9 +905,8 @@ describe('request scheduler', () => { }, }; - const dataModule = new IotAppKitDataModule({ cacheSettings: customCacheSettings }); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource, { cacheSettings: customCacheSettings }); const END = new Date(); const START = new Date(END.getTime() - HOUR_IN_MS); @@ -1138,9 +945,8 @@ describe('request scheduler', () => { }); it('stops requesting for data after unsubscribing', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { unsubscribe } = dataModule.subscribeToDataStreams( @@ -1168,9 +974,9 @@ describe('request scheduler', () => { it('periodically requests data after switching from static to duration based viewport', async () => { const DATA_POINT: DataPoint = { x: Date.now(), y: 1921 }; - const dataModule = new IotAppKitDataModule(); + const dataSource = createMockSiteWiseDataSource({ dataStreams: [{ ...DATA_STREAM, data: [DATA_POINT] }] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { update, unsubscribe } = dataModule.subscribeToDataStreams( @@ -1209,9 +1015,8 @@ describe('request scheduler', () => { }); it('stops the request scheduler when we first update request info to have duration and then call unsubscribe', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { update, unsubscribe } = dataModule.subscribeToDataStreams( @@ -1247,9 +1052,8 @@ describe('request scheduler', () => { }); it('stops the request scheduler when request info gets updated with static viewport that does not intersect with any TTL intervals', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( @@ -1277,9 +1081,8 @@ describe('request scheduler', () => { }); it('continues the schedule requests when request info gets updated with static viewport that intersects with TTL intervals', async () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const { update } = dataModule.subscribeToDataStreams( @@ -1308,9 +1111,8 @@ describe('request scheduler', () => { }); it('when data is requested from the viewport start to end with a buffer, include a buffer', () => { - const dataModule = new IotAppKitDataModule(); const dataSource = createMockSiteWiseDataSource({ dataStreams: [DATA_STREAM] }); - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const start = new Date(2021, 5, 20, 1, 30); diff --git a/packages/core/src/data-module/IotAppKitDataModule.ts b/packages/core/src/data-module/TimeSeriesDataModule.ts similarity index 90% rename from packages/core/src/data-module/IotAppKitDataModule.ts rename to packages/core/src/data-module/TimeSeriesDataModule.ts index 8f5c74c46..e6e542242 100644 --- a/packages/core/src/data-module/IotAppKitDataModule.ts +++ b/packages/core/src/data-module/TimeSeriesDataModule.ts @@ -2,8 +2,8 @@ import { MinimalViewPortConfig } from '@synchro-charts/core'; import { v4 } from 'uuid'; import SubscriptionStore from './subscription-store/subscriptionStore'; import { - DataModule, DataModuleSubscription, + DataSource, DataStreamQuery, RequestInformation, RequestInformationAndRange, @@ -33,12 +33,12 @@ interface IotAppKitDataModuleConfiguration { cacheSettings?: Partial; } -export class IotAppKitDataModule implements DataModule { +export class TimeSeriesDataModule { private dataCache: DataCache; - private subscriptions: SubscriptionStore; + private subscriptions: SubscriptionStore; - private dataSourceStore = new DataSourceStore(); + private dataSourceStore: DataSourceStore; private cacheSettings: CacheSettings; @@ -46,9 +46,10 @@ export class IotAppKitDataModule implements DataModule { * Create a new data module, optionally with a pre-hydrated data cache. * */ - constructor(configuration: IotAppKitDataModuleConfiguration = {}) { + constructor(dataSource: DataSource, configuration: IotAppKitDataModuleConfiguration = {}) { const { initialDataCache, cacheSettings } = configuration; + this.dataSourceStore = new DataSourceStore(dataSource); this.dataCache = new DataCache(initialDataCache); this.cacheSettings = { ...DEFAULT_CACHE_SETTINGS, @@ -61,8 +62,6 @@ export class IotAppKitDataModule implements DataModule { }); } - public registerDataSource = this.dataSourceStore.registerDataSource; - /** * Fulfill query * @@ -77,7 +76,7 @@ export class IotAppKitDataModule implements DataModule { }: { viewport: MinimalViewPortConfig; request: TimeSeriesDataRequest; - queries: DataStreamQuery[]; + queries: Query[]; }) => { const requestedStreams = this.dataSourceStore.getRequestsFromQueries({ queries, request }); @@ -122,7 +121,7 @@ export class IotAppKitDataModule implements DataModule { return { start, end }; }; - public subscribeToDataStreams = ( + public subscribeToDataStreams = ( { queries, request }: DataModuleSubscription, callback: (data: TimeSeriesData) => void ): SubscriptionResponse => { @@ -159,10 +158,7 @@ export class IotAppKitDataModule implements DataModule { return { unsubscribe, update }; }; - private update = ( - subscriptionId: string, - subscriptionUpdate: SubscriptionUpdate - ): void => { + private update = (subscriptionId: string, subscriptionUpdate: SubscriptionUpdate): void => { const subscription = this.subscriptions.getSubscription(subscriptionId); const updatedSubscription = { ...subscription, ...subscriptionUpdate }; @@ -181,7 +177,7 @@ export class IotAppKitDataModule implements DataModule { } }; - private registerRequest = ( + private registerRequest = ( subscription: { queries: Query[]; request: TimeSeriesDataRequest }, requestInformations: RequestInformationAndRange[] ): void => { diff --git a/packages/core/src/data-module/data-cache/caching/caching.spec.ts b/packages/core/src/data-module/data-cache/caching/caching.spec.ts index 22ca260ec..73985c902 100755 --- a/packages/core/src/data-module/data-cache/caching/caching.spec.ts +++ b/packages/core/src/data-module/data-cache/caching/caching.spec.ts @@ -9,7 +9,7 @@ import { maxCacheDuration, getRequestInformations, } from './caching'; -import { DEFAULT_CACHE_SETTINGS } from '../../IotAppKitDataModule'; +import { DEFAULT_CACHE_SETTINGS } from '../../TimeSeriesDataModule'; import { HOUR_IN_MS, MINUTE_IN_MS, SECOND_IN_MS } from '../../../common/time'; import { DataStreamsStore } from '../types'; import { IntervalStructure } from '../../../common/intervalStructure'; diff --git a/packages/core/src/data-module/data-source-store/dataSourceStore.spec.ts b/packages/core/src/data-module/data-source-store/dataSourceStore.spec.ts index 02518c3ae..1153966cc 100644 --- a/packages/core/src/data-module/data-source-store/dataSourceStore.spec.ts +++ b/packages/core/src/data-module/data-source-store/dataSourceStore.spec.ts @@ -1,18 +1,9 @@ import DataSourceStore from './dataSourceStore'; import { DataSource } from '../types'; -it('registers a data source', () => { - const dataSourceStore = new DataSourceStore(); - expect(() => - dataSourceStore.registerDataSource({ name: 'custom', initiateRequest: () => {}, getRequestsFromQuery: () => [] }) - ).not.toThrowError(); -}); - it('initiate a request on a registered data source', () => { - const dataSourceStore = new DataSourceStore(); - const customSource: DataSource = { name: 'custom', initiateRequest: jest.fn(), getRequestsFromQuery: () => [] }; - - dataSourceStore.registerDataSource(customSource); + const customSource: DataSource = { initiateRequest: jest.fn(), getRequestsFromQuery: () => [] }; + const dataSourceStore = new DataSourceStore(customSource); const query = { source: 'custom' }; @@ -38,20 +29,3 @@ it('initiate a request on a registered data source', () => { [] ); }); - -it('throws error when attempting to initiate a request to a non-existent data source', () => { - const dataSourceStore = new DataSourceStore(); - - const request = { viewport: { start: new Date(), end: new Date() }, settings: { fetchFromStartToEnd: true } }; - expect(() => - dataSourceStore.initiateRequest( - { - request, - query: { source: 'some-name' }, - onSuccess: () => {}, - onError: () => {}, - }, - [] - ) - ).toThrowError(/some-name/); -}); 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 5845b91e4..18ba11728 100644 --- a/packages/core/src/data-module/data-source-store/dataSourceStore.ts +++ b/packages/core/src/data-module/data-source-store/dataSourceStore.ts @@ -1,6 +1,5 @@ import { DataSource, - DataSourceName, DataSourceRequest, DataStreamQuery, RequestInformation, @@ -8,26 +7,14 @@ import { } from '../types'; import { TimeSeriesDataRequest } from '../data-cache/requestTypes'; -/** - * Manages the collection of registered data sources, as well as delegating requests to the correct data-source. - * - * Data sources enable queries to be made to return data streams for use throughout. - */ -export default class DataSourceStore { - // Currently, there are no data sources provided by default, but we will add defaults here as they are produced. - private dataSources: { [name: string]: DataSource } = {}; +export default class DataSourceStore { + private dataSource: DataSource; - private getDataSource = (source: DataSourceName): DataSource => { - if (this.dataSources[source] == null) { - throw new Error( - `Expected to find a data source with the name "${source}", but could not find the requested data source.` - ); - } + constructor(dataSource: DataSource) { + this.dataSource = dataSource; + } - return this.dataSources[source]; - }; - - public getRequestsFromQueries = ({ + public getRequestsFromQueries = ({ queries, request, }: { @@ -35,35 +22,19 @@ export default class DataSourceStore { request: TimeSeriesDataRequest; }): RequestInformation[] => queries.map((query) => this.getRequestsFromQuery({ query, request })).flat(); - public getRequestsFromQuery = ({ + public getRequestsFromQuery = ({ query, request, }: { query: Query; request: TimeSeriesDataRequest; }): RequestInformation[] => { - const dataSource = this.getDataSource(query.source); - return dataSource + return this.dataSource .getRequestsFromQuery({ query, request }) .map((request) => ({ ...request, cacheSettings: query.cacheSettings })); }; - public initiateRequest = ( - request: DataSourceRequest, - requestInformations: RequestInformationAndRange[] - ) => { - const dataSource = this.getDataSource(request.query.source); - dataSource.initiateRequest(request, requestInformations); - }; - - public registerDataSource = (dataSource: DataSource) => { - if (this.dataSources[dataSource.name] != null) { - throw new Error( - `Attempted to add a data source with a name of "${dataSource.name}", - but the provided data source name is already present.` - ); - } - - this.dataSources[dataSource.name] = dataSource; + public initiateRequest = (request: DataSourceRequest, requestInformations: RequestInformationAndRange[]) => { + this.dataSource.initiateRequest(request, requestInformations); }; } 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 de6bb0c8d..c50a10496 100644 --- a/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts +++ b/packages/core/src/data-module/subscription-store/subscriptionStore.spec.ts @@ -1,16 +1,14 @@ import SubscriptionStore from './subscriptionStore'; -import { SiteWiseDataStreamQuery, Subscription } from '../types'; +import { DataSource, SiteWiseDataStreamQuery, Subscription } from '../types'; import { DataCache } from '../data-cache/dataCacheWrapped'; import DataSourceStore from '../data-source-store/dataSourceStore'; -import { DEFAULT_CACHE_SETTINGS } from '../IotAppKitDataModule'; +import { DEFAULT_CACHE_SETTINGS } from '../TimeSeriesDataModule'; const createSubscriptionStore = () => { - const store = new DataSourceStore(); - store.registerDataSource({ - name: 'site-wise', + const store = new DataSourceStore({ initiateRequest: () => {}, getRequestsFromQuery: () => [], - }); + } as DataSource); return new SubscriptionStore({ dataCache: new DataCache(), @@ -21,7 +19,7 @@ const createSubscriptionStore = () => { const MOCK_SUBSCRIPTION: Subscription = { emit: () => {}, - queries: [{ source: 'site-wise', assets: [] }], + queries: [{ assets: [] }], request: { viewport: { start: new Date(2000, 0, 0), end: new Date() }, settings: { @@ -47,7 +45,6 @@ it('updates subscription', () => { const queries = [ { - source: 'site-wise', assets: [{ assetId: '123', properties: [{ propertyId: 'prop1' }, { propertyId: 'prop2' }] }], }, ]; diff --git a/packages/core/src/data-module/subscription-store/subscriptionStore.ts b/packages/core/src/data-module/subscription-store/subscriptionStore.ts index 41e45371f..a06e6e4ce 100644 --- a/packages/core/src/data-module/subscription-store/subscriptionStore.ts +++ b/packages/core/src/data-module/subscription-store/subscriptionStore.ts @@ -11,8 +11,8 @@ import { maxCacheDuration } from '../data-cache/caching/caching'; * * Manages the collection of subscriptions */ -export default class SubscriptionStore { - private dataSourceStore: DataSourceStore; +export default class SubscriptionStore { + private dataSourceStore: DataSourceStore; private dataCache: DataCache; private cacheSettings: CacheSettings; private unsubscribeMap: { [subscriberId: string]: () => void } = {}; @@ -24,7 +24,7 @@ export default class SubscriptionStore { dataCache, cacheSettings, }: { - dataSourceStore: DataSourceStore; + dataSourceStore: DataSourceStore; dataCache: DataCache; cacheSettings: CacheSettings; }) { @@ -33,7 +33,7 @@ export default class SubscriptionStore { this.cacheSettings = cacheSettings; } - addSubscription(subscriptionId: string, subscription: Subscription): void { + addSubscription(subscriptionId: string, subscription: Subscription): void { if (this.subscriptions[subscriptionId] == null) { /** * If the subscription is query based @@ -91,10 +91,7 @@ export default class SubscriptionStore { } } - updateSubscription( - subscriptionId: string, - subscriptionUpdate: SubscriptionUpdate - ): void { + updateSubscription(subscriptionId: string, subscriptionUpdate: SubscriptionUpdate): void { if (this.subscriptions[subscriptionId] == null) { throw new Error( `Attempted to update a subscription with an id of "${subscriptionId}", but the requested subscription does not exist.` diff --git a/packages/core/src/data-module/types.ts b/packages/core/src/data-module/types.ts index fe3767d54..08e22dec3 100644 --- a/packages/core/src/data-module/types.ts +++ b/packages/core/src/data-module/types.ts @@ -47,11 +47,11 @@ export interface DataStream { isLoading?: boolean; isRefreshing?: boolean; error?: ErrorDetails; + // Mechanism to associate some information about the data stream + meta?: Record; } export type DataSource = { - // An identifier for the name of the source, i.e. 'site-wise', 'roci', etc.. - name: DataSourceName; // this is unique initiateRequest: (request: DataSourceRequest, requestInformations: RequestInformationAndRange[]) => void; getRequestsFromQuery: ({ query, request }: { query: Query; request: TimeSeriesDataRequest }) => RequestInformation[]; }; @@ -80,7 +80,6 @@ export type DataModuleSubscription = { }; export type DataStreamQuery = { - source: DataSourceName; cacheSettings?: CacheSettings; }; @@ -111,7 +110,7 @@ export type DataSourceRequest = { * Adds a subscription to the data-module. * The data-module will ensure that the requested data is provided to the subscriber. */ -type SubscribeToDataStreams = ( +type SubscribeToDataStreams = ( { queries, request }: DataModuleSubscription, callback: (data: TimeSeriesData) => void ) => { @@ -119,14 +118,6 @@ type SubscribeToDataStreams = ( update: (subscriptionUpdate: SubscriptionUpdate) => void; }; -/** - * The core of the IoT App Kit, manages the data, and getting data to those who subscribe. - */ -export interface DataModule { - registerDataSource: (dataSource: DataSource) => void; - subscribeToDataStreams: SubscribeToDataStreams; -} - export type StyleSettingsMap = { [refId: string]: BaseStyleSettings }; // Style settings sharable by all components diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9b6217fab..409d6b9d1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -8,6 +8,6 @@ export * from './common/viewport'; export * from './common/time'; export * from './common/combineProviders'; -export * from './data-module/IotAppKitDataModule'; +export * from './data-module/TimeSeriesDataModule'; export * from './mockWidgetProperties'; diff --git a/packages/source-iotsitewise/src/component-session.ts b/packages/source-iotsitewise/src/component-session.ts index 493cf1854..75d400465 100644 --- a/packages/source-iotsitewise/src/component-session.ts +++ b/packages/source-iotsitewise/src/component-session.ts @@ -1,5 +1,6 @@ -import { DataModuleSession, IotAppKitDataModule, Session } from '@iot-app-kit/core'; +import { DataModuleSession, TimeSeriesDataModule, Session } from '@iot-app-kit/core'; import { SiteWiseAssetModule } from './asset-modules'; +import { SiteWiseAssetDataStreamQuery } from './time-series-data/types'; /** * Component session to manage component data module sessions. @@ -8,7 +9,7 @@ import { SiteWiseAssetModule } from './asset-modules'; export class SiteWiseComponentSession implements Session { public componentId: string; - public siteWiseTimeSeriesModule: IotAppKitDataModule; + public siteWiseTimeSeriesModule: TimeSeriesDataModule; public siteWiseAssetModule: SiteWiseAssetModule; @@ -20,7 +21,7 @@ export class SiteWiseComponentSession implements Session { siteWiseAssetModule, }: { componentId: string; - siteWiseTimeSeriesModule: IotAppKitDataModule; + siteWiseTimeSeriesModule: TimeSeriesDataModule; siteWiseAssetModule: SiteWiseAssetModule; }) { this.componentId = componentId; diff --git a/packages/source-iotsitewise/src/initialize.ts b/packages/source-iotsitewise/src/initialize.ts index 1b9dff427..ad9ca1088 100644 --- a/packages/source-iotsitewise/src/initialize.ts +++ b/packages/source-iotsitewise/src/initialize.ts @@ -1,5 +1,5 @@ import { SiteWiseTimeSeriesDataProvider } from './time-series-data/provider'; -import { IotAppKitDataModule, TreeQuery, TimeQuery, TimeSeriesData, TimeSeriesDataRequest } from '@iot-app-kit/core'; +import { TimeSeriesDataModule, TreeQuery, TimeQuery, TimeSeriesData, TimeSeriesDataRequest } from '@iot-app-kit/core'; import { SiteWiseAssetQuery, SiteWiseDataSourceSettings } from './time-series-data/types'; import { BranchReference, @@ -20,11 +20,9 @@ import { assetSession } from './sessions'; type SiteWiseDataSourceInitInputs = ( | { - registerDataSources?: boolean; iotSiteWiseClient: IoTSiteWiseClient; } | { - registerDataSources?: boolean; awsCredentials: Credentials | AWSCredentialsProvider; awsRegion: string; } @@ -47,17 +45,12 @@ export type SiteWiseQuery = { * @param awsRegion - Region for AWS based data sources to point towards, i.e. us-east-1 */ export const initialize = (input: SiteWiseDataSourceInitInputs) => { - const siteWiseTimeSeriesModule = new IotAppKitDataModule(); const siteWiseSdk = 'iotSiteWiseClient' in input ? input.iotSiteWiseClient : sitewiseSdk(input.awsCredentials, input.awsRegion); const assetDataSource: SiteWiseAssetDataSource = createSiteWiseAssetDataSource(siteWiseSdk); const siteWiseAssetModule = new SiteWiseAssetModule(assetDataSource); - - if (input.registerDataSources !== false) { - /** Automatically registered data sources */ - siteWiseTimeSeriesModule.registerDataSource(createDataSource(siteWiseSdk, input.settings)); - } + const siteWiseTimeSeriesModule = new TimeSeriesDataModule(createDataSource(siteWiseSdk, input.settings)); return { query: { @@ -66,12 +59,7 @@ export const initialize = (input: SiteWiseDataSourceInitInputs) => { new SiteWiseTimeSeriesDataProvider( new SiteWiseComponentSession({ componentId: sessionId, siteWiseTimeSeriesModule, siteWiseAssetModule }), { - queries: [ - { - source: 'site-wise', - ...assetQuery, - }, - ], + queries: [assetQuery], request: params, } ), diff --git a/packages/source-iotsitewise/src/sessions.ts b/packages/source-iotsitewise/src/sessions.ts index 94a004db5..42ec28a54 100644 --- a/packages/source-iotsitewise/src/sessions.ts +++ b/packages/source-iotsitewise/src/sessions.ts @@ -1,8 +1,11 @@ -import { IotAppKitDataModule } from '@iot-app-kit/core'; +import { TimeSeriesDataModule } from '@iot-app-kit/core'; import { SiteWiseComponentSession } from './component-session'; import { SiteWiseAssetSession } from './asset-modules'; +import { SiteWiseAssetDataStreamQuery } from './time-series-data/types'; -export const timeSeriesDataSession = (session: SiteWiseComponentSession): IotAppKitDataModule => { +export const timeSeriesDataSession = ( + session: SiteWiseComponentSession +): TimeSeriesDataModule => { return session.siteWiseTimeSeriesModule; }; diff --git a/packages/source-iotsitewise/src/time-series-data/data-source.spec.ts b/packages/source-iotsitewise/src/time-series-data/data-source.spec.ts index 62ad793e7..4e0faf102 100644 --- a/packages/source-iotsitewise/src/time-series-data/data-source.spec.ts +++ b/packages/source-iotsitewise/src/time-series-data/data-source.spec.ts @@ -1,7 +1,7 @@ import flushPromises from 'flush-promises'; import { IoTSiteWiseClient } from '@aws-sdk/client-iotsitewise'; -import { createDataSource, SITEWISE_DATA_SOURCE } from './data-source'; -import { MINUTE_IN_MS, HOUR_IN_MS, MONTH_IN_MS, IotAppKitDataModule, TimeSeriesDataRequest } from '@iot-app-kit/core'; +import { createDataSource } from './data-source'; +import { MINUTE_IN_MS, HOUR_IN_MS, MONTH_IN_MS, TimeSeriesDataModule, TimeSeriesDataRequest } from '@iot-app-kit/core'; import { SiteWiseDataStreamQuery } from './types'; import { AGGREGATE_VALUES, @@ -74,7 +74,6 @@ describe('initiateRequest', () => { onError: noop, onSuccess: noop, query: { - source: SITEWISE_DATA_SOURCE, assets: [], }, request: LAST_MINUTE_REQUEST, @@ -102,7 +101,6 @@ describe('initiateRequest', () => { const PROPERTY_2 = 'prop-2'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [{ assetId: ASSET_ID, properties: [{ propertyId: PROPERTY_1 }, { propertyId: PROPERTY_2 }] }], }; @@ -182,7 +180,6 @@ describe('initiateRequest', () => { const PROPERTY_2 = 'prop-2'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: ASSET_1, properties: [{ propertyId: PROPERTY_1 }] }, { assetId: ASSET_2, properties: [{ propertyId: PROPERTY_2 }] }, @@ -265,7 +262,6 @@ describe('initiateRequest', () => { const PROPERTY_2 = 'prop-2'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [{ assetId: ASSET_ID, properties: [{ propertyId: PROPERTY_1 }, { propertyId: PROPERTY_2 }] }], }; @@ -331,7 +327,6 @@ describe('initiateRequest', () => { const PROPERTY_2 = 'prop-2'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: ASSET_1, properties: [{ propertyId: PROPERTY_1 }] }, { assetId: ASSET_2, properties: [{ propertyId: PROPERTY_2 }] }, @@ -399,7 +394,6 @@ describe('initiateRequest', () => { const PROPERTY_2 = 'prop-2'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [{ assetId: ASSET_ID, properties: [{ propertyId: PROPERTY_1 }, { propertyId: PROPERTY_2 }] }], }; @@ -470,7 +464,6 @@ it('requests raw data if specified per asset property', async () => { const dataSource = createDataSource(mockSDK); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'some-asset-id', @@ -560,14 +553,10 @@ it('requests raw data if specified per asset property', async () => { describe('e2e through data-module', () => { describe('fetching range of historical data', () => { it('reports error occurred on request initiation', async () => { - const dataModule = new IotAppKitDataModule(); - const batchGetAssetPropertyAggregates = jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_ERROR); - const mockSDK = createMockSiteWiseSDK({ batchGetAssetPropertyAggregates }); const dataSource = createDataSource(mockSDK); - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const assetId = 'asset-id'; @@ -578,7 +567,6 @@ describe('e2e through data-module', () => { queries: [ { assets: [{ assetId, properties: [{ propertyId }] }], - source: dataSource.name, } as SiteWiseDataStreamQuery, ], request: HISTORICAL_REQUEST, @@ -605,15 +593,12 @@ describe('e2e through data-module', () => { describe('fetching latest value', () => { it('reports error occurred on request initiation', async () => { - const dataModule = new IotAppKitDataModule(); - const batchGetAssetPropertyValueHistory = jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_VALUE_HISTORY); const batchGetAssetPropertyValue = jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_ERROR); const mockSDK = createMockSiteWiseSDK({ batchGetAssetPropertyValueHistory, batchGetAssetPropertyValue }); const dataSource = createDataSource(mockSDK); - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const timeSeriesCallback = jest.fn(); const assetId = 'asset-id'; @@ -624,7 +609,6 @@ describe('e2e through data-module', () => { queries: [ { assets: [{ assetId, properties: [{ propertyId }] }], - source: dataSource.name, } as SiteWiseDataStreamQuery, ], request: { @@ -670,7 +654,6 @@ describe.skip('aggregated data', () => { const dataSource = createDataSource(mockSDK); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], }; @@ -772,7 +755,6 @@ describe.skip('aggregated data', () => { const dataSource = createDataSource(mockSDK); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }], }; @@ -864,7 +846,6 @@ describe.skip('aggregated data', () => { const resolution = '1m'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'some-asset-id', @@ -991,7 +972,6 @@ describe.skip('aggregated data', () => { const dataSource = createDataSource(mockSDK); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [], }; @@ -1034,7 +1014,6 @@ describe('gets requests from query', () => { const REF_ID = 'some-ref'; const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'asset', @@ -1059,14 +1038,11 @@ describe('gets requests from query', () => { }); it.skip('only fetches uncached data for multiple properties', async () => { - const dataModule = new IotAppKitDataModule(); - const getAssetPropertyValueHistory = jest.fn().mockResolvedValue(ASSET_PROPERTY_VALUE_HISTORY); const mockSDK = createMockSiteWiseSDK({ getAssetPropertyValueHistory }); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'some-asset-id', @@ -1076,8 +1052,7 @@ it.skip('only fetches uncached data for multiple properties', async () => { }; const dataSource = createDataSource(mockSDK); - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const START_1 = new Date(2000, 1, 1); const END_1 = new Date(2000, 2, 1); @@ -1109,7 +1084,6 @@ it.skip('only fetches uncached data for multiple properties', async () => { (getAssetPropertyValueHistory as Mock).mockClear(); const updatedQuery: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'some-asset-id', @@ -1158,14 +1132,11 @@ it.skip('only fetches uncached data for multiple properties', async () => { }); it.skip('requests buffered data', async () => { - const dataModule = new IotAppKitDataModule(); - const getAssetPropertyValueHistory = jest.fn().mockResolvedValue(ASSET_PROPERTY_VALUE_HISTORY); const mockSDK = createMockSiteWiseSDK({ getAssetPropertyValueHistory }); const query: SiteWiseDataStreamQuery = { - source: SITEWISE_DATA_SOURCE, assets: [ { assetId: 'some-asset-id', @@ -1175,8 +1146,7 @@ it.skip('requests buffered data', async () => { }; const dataSource = createDataSource(mockSDK); - - dataModule.registerDataSource(dataSource); + const dataModule = new TimeSeriesDataModule(dataSource); const START = new Date(2000, 1, 1); const BUFFERED_START = new Date(2000, 0, 4, 15, 33, 20); diff --git a/packages/source-iotsitewise/src/time-series-data/data-source.ts b/packages/source-iotsitewise/src/time-series-data/data-source.ts index 402784d47..dcf71203c 100644 --- a/packages/source-iotsitewise/src/time-series-data/data-source.ts +++ b/packages/source-iotsitewise/src/time-series-data/data-source.ts @@ -13,8 +13,6 @@ import { } from '@iot-app-kit/core'; import { SupportedResolutions } from './util/resolution'; -export const SITEWISE_DATA_SOURCE = 'site-wise'; - const DEFAULT_RESOLUTION_MAPPING = { [MINUTE_IN_MS * 15]: SupportedResolutions.ONE_MINUTE, [HOUR_IN_MS * 15]: SupportedResolutions.ONE_HOUR, @@ -66,7 +64,6 @@ export const createDataSource = ( ): DataSource => { const client = new SiteWiseClient(siteWise, settings); return { - name: SITEWISE_DATA_SOURCE, initiateRequest: ({ onSuccess, onError }, requestInformations) => Promise.all([ client.getLatestPropertyDataPoint({ onSuccess, onError, requestInformations }), diff --git a/packages/source-iotsitewise/src/time-series-data/provider.spec.ts b/packages/source-iotsitewise/src/time-series-data/provider.spec.ts index 678543442..88a851d1c 100644 --- a/packages/source-iotsitewise/src/time-series-data/provider.spec.ts +++ b/packages/source-iotsitewise/src/time-series-data/provider.spec.ts @@ -1,5 +1,5 @@ import { SiteWiseTimeSeriesDataProvider } from './provider'; -import { IotAppKitDataModule, DataSource, DataStream, MINUTE_IN_MS, DATA_STREAM } from '@iot-app-kit/core'; +import { TimeSeriesDataModule, DataSource, DataStream, MINUTE_IN_MS, DATA_STREAM } from '@iot-app-kit/core'; import { createSiteWiseAssetDataSource } from '../asset-modules/asset-data-source'; import { DESCRIBE_ASSET_RESPONSE } from '../__mocks__/asset'; import { SiteWiseComponentSession } from '../component-session'; @@ -8,14 +8,12 @@ import { createMockSiteWiseSDK } from '../__mocks__/iotsitewiseSDK'; import { SiteWiseAssetModule } from '../asset-modules'; const createMockSource = (dataStreams: DataStream[]): DataSource => ({ - name: 'site-wise', initiateRequest: jest.fn(({ onSuccess }: { onSuccess: any }) => onSuccess(dataStreams)), getRequestsFromQuery: () => dataStreams.map((dataStream) => ({ id: dataStream.id, resolution: '0' })), }); -const timeSeriesModule = new IotAppKitDataModule(); const dataSource = createMockSource([DATA_STREAM]); -timeSeriesModule.registerDataSource(dataSource); +const timeSeriesModule = new TimeSeriesDataModule(dataSource); const assetModule = new SiteWiseAssetModule( createSiteWiseAssetDataSource( @@ -46,7 +44,7 @@ it.skip('subscribes, updates, and unsubscribes to time series data by delegating const refreshRate = MINUTE_IN_MS; const provider = new SiteWiseTimeSeriesDataProvider(componentSession, { - queries: [{ source: 'site-wise', assets: [] }], + queries: [{ assets: [] }], request: { viewport: { start: START_1, end: END_1 }, settings: { fetchFromStartToEnd: true, refreshRate }, diff --git a/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.spec.ts b/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.spec.ts index dea0a258f..97c9b08ec 100644 --- a/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.spec.ts +++ b/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.spec.ts @@ -1,7 +1,7 @@ import { subscribeToTimeSeriesData } from './subscribeToTimeSeriesData'; import { createSiteWiseAssetDataSource } from '../asset-modules/asset-data-source'; import { createMockSiteWiseSDK } from '../__mocks__/iotsitewiseSDK'; -import { IotAppKitDataModule } from '@iot-app-kit/core'; +import { TimeSeriesDataModule } from '@iot-app-kit/core'; import { IoTSiteWiseClient } from '@aws-sdk/client-iotsitewise'; import flushPromises from 'flush-promises'; import { createDataSource } from './data-source'; @@ -14,8 +14,7 @@ const initializeSubscribeToTimeSeriesData = (client: IoTSiteWiseClient) => { const assetDataSource: SiteWiseAssetDataSource = createSiteWiseAssetDataSource(client); const siteWiseAssetModule = new SiteWiseAssetModule(assetDataSource); const siteWiseAssetModuleSession = siteWiseAssetModule.startSession(); - const dataModule = new IotAppKitDataModule(); - dataModule.registerDataSource(createDataSource(client)); + const dataModule = new TimeSeriesDataModule(createDataSource(client)); return subscribeToTimeSeriesData(dataModule, siteWiseAssetModuleSession); }; @@ -35,7 +34,7 @@ it('unsubscribes', () => { const assetDataSource: SiteWiseAssetDataSource = createSiteWiseAssetDataSource(createMockSiteWiseSDK()); const siteWiseAssetModule = new SiteWiseAssetModule(assetDataSource); const siteWiseAssetModuleSession = siteWiseAssetModule.startSession(); - const dataModule = new IotAppKitDataModule(); + const dataModule = new TimeSeriesDataModule(createDataSource(createMockSiteWiseSDK())); const unsubscribeSpy = jest.fn(); jest.spyOn(dataModule, 'subscribeToDataStreams').mockImplementation(() => ({ @@ -85,7 +84,6 @@ it('provides time series data from iotsitewise', async () => { { queries: [ { - source: 'site-wise', assets: [ { assetId: ASSET_ID, @@ -187,7 +185,6 @@ it('provides timeseries data from iotsitewise when subscription is updated', asy update({ queries: [ { - source: 'site-wise', assets: [ { assetId: ASSET_ID, diff --git a/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.ts b/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.ts index c0ca566dc..90150ea4a 100644 --- a/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.ts +++ b/packages/source-iotsitewise/src/time-series-data/subscribeToTimeSeriesData.ts @@ -4,7 +4,7 @@ import { SiteWiseDataStreamQuery } from './types'; import { MinimalViewPortConfig } from '@synchro-charts/core'; import { ErrorDetails, - DataModule, + TimeSeriesDataModule, DataModuleSubscription, DataStream, TimeSeriesData, @@ -13,7 +13,7 @@ import { import { SiteWiseAssetSession } from '../asset-modules'; export const subscribeToTimeSeriesData = - (dataModule: DataModule, assetModuleSession: SiteWiseAssetSession) => + (dataModule: TimeSeriesDataModule, assetModuleSession: SiteWiseAssetSession) => ({ queries, request }: DataModuleSubscription, callback: (data: TimeSeriesData) => void) => { let dataStreams: DataStream[] = []; diff --git a/yarn.lock b/yarn.lock index e9d83e11a..1f72b62e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5063,6 +5063,37 @@ uuid "^3.3.2" validator "^13.6.0" +"@synchro-charts/core@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@synchro-charts/core/-/core-6.0.1.tgz#b82788d027b598dab68862e17332398ceed3fd53" + integrity sha512-I0r2ckc8Bv1EKjv16TTb+0TVI5yqyVrIh6dZ1AopdpiIJO00jlxHDO7QtJnvXtjAbO4pThsDspRB88uXMiuaoQ== + dependencies: + "@stencil/redux" "^0.1.1" + "@types/d3" "^5.16.4" + d3-array "^2.3.2" + d3-axis "^1.0.12" + d3-brush "^1.1.3" + d3-drag "^1.2.5" + d3-scale "^3.2.0" + d3-selection "^1.3.1" + d3-zoom "^1.8.3" + detect-browser "^5.0.0" + immutability-helper "^3.0.1" + lodash.clonedeep "^4.5.0" + lodash.isequal "^4.5.0" + lodash.isnumber "^3.0.3" + lodash.round "^4.0.4" + lodash.throttle "^4.1.1" + lodash.uniq "^4.5.0" + lodash.uniqby "^4.7.0" + parse-duration "^1.0.0" + redux "^4.0.4" + resize-observer-polyfill "^1.5.1" + three "^0.125.0" + tippy.js "^5.2.0" + uuid "^3.3.2" + validator "^13.6.0" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -5917,11 +5948,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== -"@types/node@^14.0.10", "@types/node@^14.14.31": +"@types/node@^14.0.10": version "14.18.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24" integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A== +"@types/node@^14.14.31": + version "14.18.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.24.tgz#406b220dc748947e1959d8a38a75979e87166704" + integrity sha512-aJdn8XErcSrfr7k8ZDDfU6/2OgjZcB2Fu9d+ESK8D7Oa5mtsv8Fa8GpcwTA0v60kuZBaalKPzuzun4Ov1YWO/w== + "@types/node@^15.12.2": version "15.14.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" @@ -6935,9 +6971,9 @@ ansi-colors@^3.0.0: integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: version "3.2.0" @@ -8953,9 +8989,9 @@ colord@^2.9.1, colord@^2.9.2: integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== colorette@^2.0.16: - version "2.0.16" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== colors@1.4.0: version "1.4.0" @@ -9866,9 +9902,9 @@ cypress@^6.9.1: yauzl "^2.10.0" cypress@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.6.1.tgz#a7d6b5a53325b3dc4960181f5800a5ade0f085eb" - integrity sha512-ECzmV7pJSkk+NuAhEw6C3D+RIRATkSb2VAHXDY6qGZbca/F9mv5pPsj2LO6Ty6oIFVBTrwCyL9agl28MtJMe2g== + version "9.7.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.7.0.tgz#bf55b2afd481f7a113ef5604aa8b693564b5e744" + integrity sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -10147,7 +10183,12 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.10.4, dayjs@^1.9.3: +dayjs@^1.10.4: + version "1.11.5" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93" + integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA== + +dayjs@^1.9.3: version "1.11.0" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== @@ -11369,11 +11410,16 @@ event-pubsub@4.3.0: resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== -eventemitter2@^6.4.2, eventemitter2@^6.4.3: +eventemitter2@^6.4.2: version "6.4.5" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655" integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw== +eventemitter2@^6.4.3: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -19748,7 +19794,7 @@ proxy-addr@~2.0.7: proxy-from-env@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" - integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== prr@~1.0.1: version "1.0.1" @@ -20972,13 +21018,20 @@ rxjs@^6.2.0, rxjs@^6.3.3, rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.4.0, rxjs@^7.5.1: +rxjs@^7.4.0: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" +rxjs@^7.5.1: + version "7.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" + integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== + dependencies: + tslib "^2.1.0" + sade@^1.7.4: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"