Skip to content

Commit

Permalink
feat: customizable resolutions (#23)
Browse files Browse the repository at this point in the history
* added customizable resolutions for sitewise data source which means we can specify a resolution per asset property
* refactored resolutionMapping to resolution and allowed to pass string if we want a specific resolution for all queries

Co-authored-by: Norbert Nader <nnader@amazon.com>
  • Loading branch information
NorbertNader and NorbertNader committed Dec 21, 2021
1 parent 8f26b58 commit 0ffd474
Show file tree
Hide file tree
Showing 22 changed files with 619 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const barChartSpecPage = async (propOverrides: Partial<Components.IotBarChart> =
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery,
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ it('provides data streams', async () => {
renderFunc,
query: {
source: 'test-mock',
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
} as SiteWiseDataStreamQuery,
});

Expand Down Expand Up @@ -87,7 +87,7 @@ it('updates with new query', async () => {

connector.query = {
source: 'test-mock',
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
} as SiteWiseDataStreamQuery;

await page.waitForChanges();
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/components/iot-kpi/iot-kpi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const kpiSpecPage = async (propOverrides: Partial<Components.IotKpi> = {}) => {
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const lineChartSpecPage = async (propOverrides: Partial<Components.IotKpi> = {})
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const scatterChartSpecPage = async (propOverrides: Partial<Components.IotScatter
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const statusGridSpecPage = async (propOverrides: Partial<Components.IotKpi> = {}
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const statusTimelineSpecPage = async (propOverrides: Partial<Components.IotStatu
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const tableSpecPage = async (propOverrides: Partial<Components.IotKpi> = {}) =>
isEditing: false,
query: {
source: 'test-mock',
assets: [{ assetId: 'some-asset-id', propertyIds: ['some-property-id'] }],
assets: [{ assetId: 'some-asset-id', properties: [{ propertyId: 'some-property-id' }] }],
} as SiteWiseDataStreamQuery, // static casting because of legacy sw
viewport,
...propOverrides,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/testing/createMockSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { toDataStreamId } from './dataStreamId';

const dataStreamIds = (query: SiteWiseDataStreamQuery) =>
query.assets
.map(({ assetId, propertyIds }) => propertyIds.map((propertyId) => toDataStreamId({ assetId, propertyId })))
.map(({ assetId, properties }) => properties.map(({ propertyId }) => toDataStreamId({ assetId, propertyId })))
.flat();

export const createMockSource = (dataStreams: DataStream[]): DataSource => ({
Expand Down
13 changes: 10 additions & 3 deletions packages/components/src/testing/testing-ground/siteWiseQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const DEMO_TURBINE_ASSET_1_PROPERTY_4 = '8d9ed440-a8dd-48bd-a35f-70db6f2e

export const STRING_QUERY = {
source: 'site-wise',
assets: [{ assetId: STRING_ASSET_ID, propertyIds: [STRING_PROPERTY_ID] }],
assets: [{ assetId: STRING_ASSET_ID, properties: [{ propertyId: STRING_PROPERTY_ID }] }],
};

export const ASSET_DETAILS_QUERY = {
Expand All @@ -22,17 +22,24 @@ export const NUMBER_QUERY = {
assets: [
{
assetId: DEMO_TURBINE_ASSET_1,
propertyIds: [DEMO_TURBINE_ASSET_1_PROPERTY_1, DEMO_TURBINE_ASSET_1_PROPERTY_4],
properties: [{ propertyId: DEMO_TURBINE_ASSET_1_PROPERTY_1 }, { propertyId: DEMO_TURBINE_ASSET_1_PROPERTY_4 }],
},
],
};

const AGGREGATED_DATA_ASSET = '099b1330-83ff-4fec-b165-c7186ec8eb23';
const AGGREGATED_DATA_PROPERTY = '05c5c47f-fd92-4823-828e-09ce63b90569';
const AGGREGATED_DATA_PROPERTY_2 = '11d2599a-2547-451d-ab79-a47f878dbbe3';

export const AGGREGATED_DATA_QUERY = {
source: 'site-wise',
assets: [{ assetId: AGGREGATED_DATA_ASSET, propertyIds: [AGGREGATED_DATA_PROPERTY] }],
assets: [{
assetId: AGGREGATED_DATA_ASSET,
properties: [
{ propertyId: AGGREGATED_DATA_PROPERTY },
{ propertyId: AGGREGATED_DATA_PROPERTY_2, resolution: '1m' }
]
}],
};

// From demo turbine asset, found at https://p-rlvy2rj8.app.iotsitewise.aws/
Expand Down
42 changes: 36 additions & 6 deletions packages/components/src/testing/testing-ground/testing-ground.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, h } from '@stencil/core';
import { initialize, DataModule } from '@iot-app-kit/core';
import { Component, State, h } from '@stencil/core';
import { initialize, DataModule, ResolutionConfig } from '@iot-app-kit/core';
import {
ASSET_DETAILS_QUERY,
DEMO_TURBINE_ASSET_1,
Expand All @@ -15,7 +15,7 @@ const VIEWPORT = { duration: '5m' };

const THREE_MINUTES = 1000 * 60 * 3;

const resolutionMapping = {
const DEFAULT_RESOLUTION_MAPPING = {
[THREE_MINUTES]: '1m',
}

Expand All @@ -24,10 +24,30 @@ const resolutionMapping = {
styleUrl: 'testing-ground.css',
})
export class TestingGround {
@State() resolution: ResolutionConfig = DEFAULT_RESOLUTION_MAPPING;
@State() viewport: { duration: string } = VIEWPORT;
private dataModule: DataModule;

componentWillLoad() {
this.dataModule = initialize({ awsCredentials: getEnvCredentials(), awsRegion: 'us-west-2' });
this.dataModule = initialize({ awsCredentials: getEnvCredentials(), awsRegion: 'us-east-1' });
}

private changeResolution = (ev: Event) => {
const resolution = (ev.target as HTMLSelectElement)?.value;

if (resolution === 'auto'){
this.resolution = DEFAULT_RESOLUTION_MAPPING;
} else if (resolution === '0') {
this.resolution = {};
} else {
this.resolution = resolution;
}
}

private changeDuration = (ev: Event) => {
const duration = `${(ev.target as HTMLSelectElement)?.value}m`;

this.viewport = { duration };
}

render() {
Expand Down Expand Up @@ -86,12 +106,22 @@ export class TestingGround {
/>
</div>
</div>
resolution: <select onChange={this.changeResolution}>
<option value={'0'}>raw</option>
<option value={'1m'}>1m</option>
<option selected value={'auto'}>auto</option>
</select>
viewport: <select onChange={this.changeDuration}>
<option value={'1'}>1 minute</option>
<option value={'3'}>3 minutes</option>
<option selected value={'5'}>5 minutes</option>
</select>
<div style={{ width: '400px', height: '500px' }}>
<iot-line-chart
appKit={this.dataModule}
query={AGGREGATED_DATA_QUERY}
viewport={VIEWPORT}
requestConfig={{ resolutionMapping, fetchAggregatedData: true }}
viewport={this.viewport}
requestConfig={{ resolution: this.resolution, fetchAggregatedData: true }}
/>
</div>
<iot-asset-details query={ASSET_DETAILS_QUERY} />
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/data-module/IotAppKitDataModule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const DATA_STREAM_QUERY: SiteWiseDataStreamQuery = {
assets: [
{
assetId: ASSET_ID,
propertyIds: [PROPERTY_ID],
properties: [{ propertyId: PROPERTY_ID }],
},
],
};
Expand All @@ -39,8 +39,8 @@ const createMockSiteWiseDataSource = (
initiateRequest: jest.fn(({ onSuccess }: DataSourceRequest<SiteWiseDataStreamQuery>) => onSuccess(dataStreams)),
getRequestsFromQuery: ({ query }) =>
query.assets
.map(({ assetId, propertyIds }) =>
propertyIds.map((propertyId) => ({
.map(({ assetId, properties }) =>
properties.map(({ propertyId }) => ({
id: toDataStreamId({ assetId, propertyId }),
resolution,
}))
Expand Down Expand Up @@ -183,7 +183,7 @@ it('subscribes to a single data stream', async () => {
assets: [
{
assetId,
propertyIds: [propertyId],
properties: [{ propertyId }],
},
],
},
Expand Down Expand Up @@ -233,7 +233,7 @@ it('requests data from a custom data source', () => {
dataModule.subscribeToDataStreams(
{
query: {
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
source: customSource.name,
},
requestInfo: {
Expand Down Expand Up @@ -778,3 +778,4 @@ it('requests data range with buffer', () => {

unsubscribe();
});

6 changes: 4 additions & 2 deletions packages/core/src/data-module/data-cache/requestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ export type OnRequestData = (opts: {
dataStreamId: string;
}) => void;

export type ResolutionMapping ={
export type ResolutionMapping = {
[viewportDuration: number]: number | string;
};

export type ResolutionConfig = ResolutionMapping | string;

export interface RequestConfig {
fetchMostRecentBeforeStart?: boolean;
requestBuffer?: number;
fetchAggregatedData?: boolean;
resolutionMapping?: ResolutionMapping;
resolution?: ResolutionConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ it('updates subscription', () => {

const query = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId: '123', propertyIds: ['prop1', 'prop2'] }],
assets: [{ assetId: '123', properties: [{ propertyId: 'prop1' }, { propertyId: 'prop2' }] }],
};

subscriptionStore.addSubscription(SUBSCRIPTION_ID, MOCK_SUBSCRIPTION);
Expand Down
40 changes: 34 additions & 6 deletions packages/core/src/data-sources/site-wise/client/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('getHistoricalPropertyDataPoints', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};

const client = new SiteWiseClient(createSiteWiseSDK({ getAssetPropertyValueHistory }));
Expand All @@ -47,7 +47,7 @@ describe('getHistoricalPropertyDataPoints', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};

const client = new SiteWiseClient(createSiteWiseSDK({ getAssetPropertyValueHistory }));
Expand Down Expand Up @@ -91,7 +91,7 @@ describe('getLatestPropertyDataPoint', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};

const client = new SiteWiseClient(createSiteWiseSDK({ getAssetPropertyValue }));
Expand Down Expand Up @@ -126,7 +126,7 @@ describe('getLatestPropertyDataPoint', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};

await client.getLatestPropertyDataPoint({ query, onSuccess, onError });
Expand All @@ -147,7 +147,7 @@ describe('getAggregatedPropertyDataPoints', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};

const client = new SiteWiseClient(createSiteWiseSDK({ getAssetPropertyAggregates }));
Expand All @@ -170,6 +170,34 @@ describe('getAggregatedPropertyDataPoints', () => {
expect(onError).toBeCalled();
});

it('throws error when no resolution specified', async () => {
const getAssetPropertyAggregates = jest.fn().mockResolvedValue(AGGREGATE_VALUES);
const assetId = 'some-asset-id';
const propertyId = 'some-property-id';

const onSuccess = jest.fn();
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, properties: [{ propertyId }] }],
};

const client = new SiteWiseClient(createSiteWiseSDK({ getAssetPropertyAggregates }));

const startDate = new Date(2000, 0, 0);
const endDate = new Date(2001, 0, 0);
const aggregateTypes = [AggregateType.AVERAGE];

await expect(async () => { await client.getAggregatedPropertyDataPoints({
query,
onSuccess,
onError,
start: startDate,
end: endDate,
aggregateTypes,
})}).rejects.toThrowError();
});

it('returns data point on success', async () => {
const assetId = 'some-asset-id';
const propertyId = 'some-property-id';
Expand All @@ -178,7 +206,7 @@ describe('getAggregatedPropertyDataPoints', () => {
const onError = jest.fn();
const query: SiteWiseDataStreamQuery = {
source: SITEWISE_DATA_SOURCE,
assets: [{ assetId, propertyIds: [propertyId] }],
assets: [{ assetId, properties: [{ propertyId }] }],
};
const getAssetPropertyAggregates = jest.fn().mockResolvedValue(AGGREGATE_VALUES);

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/data-sources/site-wise/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ export class SiteWiseClient {
query: SiteWiseDataStreamQuery;
start: Date;
end: Date;
resolution: string;
resolution?: string;
aggregateTypes: AggregateType[];
maxResults?: number;
onError: Function;
onError: ErrorCallback;
onSuccess: DataStreamCallback;
}): Promise<void> {
return getAggregatedPropertyDataPoints({ client: this.siteWiseSdk, ...options });
Expand Down

0 comments on commit 0ffd474

Please sign in to comment.