Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: batch API for historical, aggregated, and latest value data #137

Merged
merged 1 commit into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ __diff_output__
# Cypress screenshots
**/cypress/screenshots
**/cypress/videos
**/cypress/snapshots/All Specs

# Local development hard-coded credentials for use with the AWS SDK.
creds.json
Expand Down
1 change: 1 addition & 0 deletions .stylelintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
www/
loader/
node_modules/
coverage/
28 changes: 28 additions & 0 deletions docs/AWSIoTSiteWiseSource.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ You can download the AWS IoT SiteWise source from the following location: https:

To set up the AWS IoT SiteWise source, follow the instructions in [Getting started with IoT Application Kit](https://github.com/awslabs/iot-app-kit/tree/main/docs/GettingStarted.md).

---

## Queries

The AWS IoT SiteWise source provides queries that you can use to filter AWS IoT SiteWise data and assets.
Expand All @@ -34,6 +36,8 @@ query.timeSeriesData({

This query for time series data, can then be provided to any of the IoT App Kit components that support time series data.

---

## API

### `timeSeriesData`
Expand Down Expand Up @@ -140,6 +144,7 @@ const { query } = initialize({ iotsitewiseClient });
]}
/>
```
---

### `assetTree`

Expand Down Expand Up @@ -206,3 +211,26 @@ Type: Boolean

Type: Boolean

---

## SiteWiseDataSourceSettings

(Optional) Settings that can be provided when initializing the AWS IoT SiteWise source.

```
import { initialize } from '@iot-app-kit/source-iotsitewise';

const { IoTSiteWiseClient } = require("@aws-sdk/client-iotsitewise");

const iotsitewiseClient = new IoTSiteWiseClient({ region: "REGION" });

const { query } = initialize({ iotsitewiseClient, settings: { batchDuration: 100 } });
```

`batchDuration`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the preceding paragraph should be re-arranged a little. The very first word should be (Optional) [basic description]


(Optional) Timeframe over which to coalesce time-series data requests before executing a batch request, specified in ms. e.g. a `batchDuration` of 100 will cause the AWS IoT SiteWise source to repeatedly batch all requests that occur within a 100 ms timeframe.

Type: Number

The AWS IoT SiteWise source communicates with SiteWise using batch APIs to reduce network overhead. By default, all individual requests for time-series data that occur within a single frame of execution are coalesced and executed in a batch request. This behaviour is scheduled using the [Job and JobQueue](https://262.ecma-international.org/6.0/#sec-jobs-and-job-queues) concepts. Depending on dashboard configuration, widget configuration, latency, and a multitude of other factors, batching on a single frame of execution might not be desirable.
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { newSpecPage } from '@stencil/core/testing';
import { MinimalLiveViewport } from '@synchro-charts/core';
import flushPromises from 'flush-promises';
import { initialize, createMockSiteWiseSDK } from '@iot-app-kit/source-iotsitewise';
import {
initialize,
createMockSiteWiseSDK,
BATCH_ASSET_PROPERTY_VALUE_HISTORY,
BATCH_ASSET_PROPERTY_DOUBLE_VALUE,
} from '@iot-app-kit/source-iotsitewise';
import { IotTimeSeriesConnector } from './iot-time-series-connector';
import { update } from '../../testing/update';
import { CustomHTMLElement } from '../../testing/types';
Expand Down Expand Up @@ -149,6 +154,8 @@ it('populates the name, unit, and data type from the asset model information fro
Promise.resolve(createAssetResponse({ assetId: assetId as string, assetModelId })),
describeAssetModel: ({ assetModelId }) =>
Promise.resolve(createAssetModelResponse({ assetModelId: assetModelId as string, propertyId: propertyId_1 })),
batchGetAssetPropertyValueHistory: jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_VALUE_HISTORY),
batchGetAssetPropertyValue: jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_DOUBLE_VALUE),
}),
});

Expand Down Expand Up @@ -188,6 +195,8 @@ it('populates the name, unit, and data type from the asset model information fro
Promise.resolve(createAssetResponse({ assetId: assetId as string, assetModelId })),
describeAssetModel: ({ assetModelId }) =>
Promise.resolve(createAssetModelResponse({ assetModelId: assetModelId as string, propertyId: propertyId_1 })),
batchGetAssetPropertyValueHistory: jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_VALUE_HISTORY),
batchGetAssetPropertyValue: jest.fn().mockResolvedValue(BATCH_ASSET_PROPERTY_DOUBLE_VALUE),
}),
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { renderChart } from '../../testing/renderChart';
import { mockGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockBatchGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { ScaleConfig, ScaleType } from '@synchro-charts/core';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';
Expand All @@ -14,13 +14,17 @@ describe('bar chart', () => {
const assetId = 'some-asset-id';
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/aggregates?*', (req) => {
beforeEach(() => {
cy.intercept('/properties/batch/aggregates', (req) => {
const { startDate, endDate, resolution } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(req.query.startDate),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
resolution,
})
);
}).as('getAggregates');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { renderChart, testChartContainerClassNameSelector } from '../../testing/renderChart';
import { mockGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockBatchGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';

Expand All @@ -14,39 +14,48 @@ describe('handles gestures', () => {
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/history?*', (req) => {
if (new Date(req.query.startDate).getUTCFullYear() === 1899) {
cy.intercept('/properties/batch/history', (req) => {
const { startDate, endDate } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

if (new Date(startDateInMs).getUTCFullYear() === 1899) {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(new Date(req.query.endDate).getTime() - SECOND_IN_MS),
endDate: new Date(req.query.endDate),
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(new Date(endDateInMs).getTime() - SECOND_IN_MS),
endDate: new Date(endDateInMs),
})
);
} else {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(req.query.startDate),
endDate: new Date(req.query.endDate),
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
entryId: '1-0',
})
);
}
});

cy.intercept('/properties/aggregates?*', (req) => {
if (new Date(req.query.startDate).getUTCFullYear() === 1899) {
cy.intercept('/properties/batch/aggregates', (req) => {
const { startDate, endDate, resolution } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

if (new Date(startDateInMs).getUTCFullYear() === 1899) {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(new Date(req.query.endDate).getTime() - 60 * SECOND_IN_MS),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(new Date(endDateInMs).getTime() - 60 * SECOND_IN_MS),
endDate: new Date(endDateInMs),
resolution,
})
);
} else {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(req.query.startDate),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
resolution,
})
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { renderChart } from '../../testing/renderChart';
import { mockLatestValueResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import {
mockBatchLatestValueResponse,
mockBatchGetAggregatedOrRawResponse,
} from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';

Expand All @@ -13,9 +16,22 @@ describe('kpi', () => {
const assetId = 'some-asset-id';
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/latest?*', (req) => {
req.reply(mockLatestValueResponse());
beforeEach(() => {
cy.intercept('/properties/batch/history', (req) => {
const { startDate, endDate } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

req.reply(
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
})
);
}).as('getHistory');

cy.intercept('/properties/batch/latest', (req) => {
req.reply(mockBatchLatestValueResponse());
}).as('getAggregates');

cy.intercept(`/assets/${assetId}`, (req) => {
Expand All @@ -30,7 +46,7 @@ describe('kpi', () => {
it('renders', () => {
renderChart({ chartType: 'iot-kpi', settings: { resolution: '0' }, viewport: { duration: '1m' } });

cy.wait(['@getAggregates', '@getAssetSummary', '@getAssetModels']);
cy.wait(['@getAggregates', '@getAssetSummary', '@getAssetModels', '@getHistory']);

cy.matchImageSnapshot(snapshotOptions);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { renderChart } from '../../testing/renderChart';
import { mockGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockBatchGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { ScaleConfig, ScaleType } from '@synchro-charts/core';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';
Expand All @@ -14,22 +14,26 @@ describe('line chart', () => {
const assetId = 'some-asset-id';
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/aggregates?*', (req) => {
if (new Date(req.query.startDate).getUTCFullYear() === 1899) {
beforeEach(() => {
cy.intercept('/properties/batch/aggregates', (req) => {
const { startDate, endDate, resolution } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

if (new Date(startDateInMs).getUTCFullYear() === 1899) {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(new Date(req.query.endDate).getTime() - 60 * SECOND_IN_MS),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(new Date(endDateInMs).getTime() - 60 * SECOND_IN_MS),
endDate: new Date(endDateInMs),
resolution,
})
);
} else {
req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(req.query.startDate),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
resolution,
})
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { renderChart } from '../../testing/renderChart';
import { mockGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockBatchGetAggregatedOrRawResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { ScaleConfig, ScaleType } from '@synchro-charts/core';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';
Expand All @@ -14,13 +14,17 @@ describe('scatter chart', () => {
const assetId = 'some-asset-id';
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/aggregates?*', (req) => {
beforeEach(() => {
cy.intercept('/properties/batch/aggregates', (req) => {
const { startDate, endDate, resolution } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

req.reply(
mockGetAggregatedOrRawResponse({
startDate: new Date(req.query.startDate),
endDate: new Date(req.query.endDate),
resolution: req.query.resolution as string,
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
resolution,
})
);
}).as('getAggregates');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { renderChart } from '../../testing/renderChart';
import { mockLatestValueResponse } from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import {
mockBatchLatestValueResponse,
mockBatchGetAggregatedOrRawResponse,
} from '../../testing/mocks/mockGetAggregatedOrRawResponse';
import { mockGetAssetSummary } from '../../testing/mocks/mockGetAssetSummaries';
import { COMPARISON_OPERATOR } from '@synchro-charts/core';
import { mockGetAssetModelSummary } from '../../testing/mocks/mockGetAssetModelSummary';
Expand All @@ -14,9 +17,22 @@ describe('status grid', () => {
const assetId = 'some-asset-id';
const assetModelId = 'some-asset-model-id';

before(() => {
cy.intercept('/properties/latest?*', (req) => {
req.reply(mockLatestValueResponse());
beforeEach(() => {
cy.intercept('/properties/batch/history', (req) => {
const { startDate, endDate } = req.body.entries[0];
const startDateInMs = startDate * SECOND_IN_MS;
const endDateInMs = endDate * SECOND_IN_MS;

req.reply(
mockBatchGetAggregatedOrRawResponse({
startDate: new Date(startDateInMs),
endDate: new Date(endDateInMs),
})
);
});

cy.intercept('/properties/batch/latest', (req) => {
req.reply(mockBatchLatestValueResponse());
}).as('getAggregates');

cy.intercept(`/assets/${assetId}`, (req) => {
Expand Down
Loading