Skip to content

Commit

Permalink
feat: Wrap SiteWise Asset related API calls in a Data Source
Browse files Browse the repository at this point in the history
* Created a DataSource for SiteWise assets module to use. This scopes down what the asset modules can get at from the raw API.
* Use RequestProcessorWorkerGroup for every request in RequestProcessor, not any simultaneous requests for the same data item will result in only 1 API call.
* Unit testing for the RequestProcessor and RequestProcessorWorkerGroup
* Raise coverage limits back up to 80%
* Changed the base class for RequestProcessWorker to ReplaySubject. This stops an initial 'empty' value from being sent to the caller which could be confusing and made programming more complex for the non-list use-case.
  • Loading branch information
gareth-amazon authored Jan 11, 2022
1 parent ffbbe19 commit b9eabc2
Show file tree
Hide file tree
Showing 13 changed files with 664 additions and 122 deletions.
159 changes: 155 additions & 4 deletions packages/core/src/asset-modules/sitewise/requestProcessor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,160 @@
import { RequestProcessor } from './requestProcessor';
import { IoTSiteWiseClient } from '@aws-sdk/client-iotsitewise';
import { SiteWiseAssetCache } from './cache';
import { SiteWiseAssetDataSource } from '../../data-module/types';
import {
AssetSummary,
AssetPropertyValue,
DescribeAssetCommandInput,
DescribeAssetCommandOutput,
DescribeAssetModelResponse,
DescribeAssetModelCommandInput,
DescribeAssetModelCommandOutput,
GetAssetPropertyValueCommandInput,
GetAssetPropertyValueCommandOutput,
ListAssetsCommandInput,
ListAssetsCommandOutput,
ListAssociatedAssetsCommandInput,
ListAssociatedAssetsCommandOutput,
} from '@aws-sdk/client-iotsitewise';
import { Observable } from 'rxjs';
import { sampleAssetModel, sampleAssetSummary, samplePropertyValue } from '../mocks.spec';
import { HIERARCHY_ROOT_ID, HierarchyAssetSummaryList, LoadingStateEnum } from './types';

it('initializes', () => {
expect(
() => new RequestProcessor(new IoTSiteWiseClient({ region: 'us-east' }), new SiteWiseAssetCache())
).not.toThrowError();
expect(() => {
new RequestProcessor({} as SiteWiseAssetDataSource, new SiteWiseAssetCache());
}).not.toThrowError();
});

const createMockSiteWiseAssetDataSource = (): SiteWiseAssetDataSource => {
return {
describeAsset: (input: DescribeAssetCommandInput): Promise<DescribeAssetCommandOutput> => {
throw 'No Calls Expected';
},

getPropertyValue: (input: GetAssetPropertyValueCommandInput): Promise<GetAssetPropertyValueCommandOutput> => {
throw 'No Calls Expected';
},

describeAssetModel: (input: DescribeAssetModelCommandInput): Promise<DescribeAssetModelCommandOutput> => {
throw 'No Calls Expected';
},

listAssets: (input: ListAssetsCommandInput): Promise<ListAssetsCommandOutput> => {
throw 'No Calls Expected';
},

listAssociatedAssets: (input: ListAssociatedAssetsCommandInput): Promise<ListAssociatedAssetsCommandOutput> => {
throw 'No Calls Expected';
},
};
};

describe('Request an AssetSummary', () => {
const mockDataSource = createMockSiteWiseAssetDataSource();
let mockDescribeAsset = jest.fn();
mockDescribeAsset.mockReturnValue(Promise.resolve(sampleAssetSummary));
mockDataSource.describeAsset = mockDescribeAsset;

const requestProcessor: RequestProcessor = new RequestProcessor(mockDataSource, new SiteWiseAssetCache());
const observable: Observable<AssetSummary> = new Observable<AssetSummary>((observer) => {
requestProcessor.getAssetSummary({ assetId: sampleAssetSummary.id as string }, observer);
});

it('waits for the AssetSummary', (done) => {
observable.subscribe((result) => {
expect(result).not.toBeUndefined();
expect(result).toEqual(sampleAssetSummary);
done();
});
});
});

describe('Request an Asset Model', () => {
const mockDataSource = createMockSiteWiseAssetDataSource();
let mockDescribeAssetModel = jest.fn();
mockDescribeAssetModel.mockReturnValue(Promise.resolve(sampleAssetModel));
mockDataSource.describeAssetModel = mockDescribeAssetModel;

const requestProcessor: RequestProcessor = new RequestProcessor(mockDataSource, new SiteWiseAssetCache());
const observable: Observable<DescribeAssetModelResponse> = new Observable<DescribeAssetModelResponse>((observer) => {
requestProcessor.getAssetModel({ assetModelId: sampleAssetModel.assetModelId as string }, observer);
});

it('waits for the Asset Model', (done) => {
observable.subscribe((result) => expect(sampleAssetModel).toEqual(result));
done();
});
});

describe('Request an Asset Property Value', () => {
const mockDataSource = createMockSiteWiseAssetDataSource();
let mockGetPropertyValue = jest.fn();
mockGetPropertyValue.mockReturnValue(Promise.resolve(samplePropertyValue));
mockDataSource.getPropertyValue = mockGetPropertyValue;

const requestProcessor: RequestProcessor = new RequestProcessor(mockDataSource, new SiteWiseAssetCache());
const observable: Observable<AssetPropertyValue> = new Observable<AssetPropertyValue>((observer) => {
requestProcessor.getAssetPropertyValue(
{
assetId: sampleAssetSummary.id as string,
propertyId: 'doesnt matter',
},
observer
);
});

it('waits for the Asset Property Value', (done) => {
observable.subscribe((result) => expect(samplePropertyValue).toEqual(result));
done();
});
});

describe('Request an Asset Hierarchy of a parent Asset', () => {
const mockDataSource = createMockSiteWiseAssetDataSource();
const mockListAssociatedAssets = jest.fn();
const result: ListAssetsCommandOutput = { $metadata: {}, assetSummaries: [sampleAssetSummary] };
mockListAssociatedAssets.mockReturnValue(Promise.resolve<ListAssetsCommandOutput>(result));
mockDataSource.listAssociatedAssets = mockListAssociatedAssets;
const requestProcessor: RequestProcessor = new RequestProcessor(mockDataSource, new SiteWiseAssetCache());
const observable: Observable<HierarchyAssetSummaryList> = new Observable<HierarchyAssetSummaryList>((observer) => {
requestProcessor.getAssetHierarchy({ assetId: 'parentAssetId', assetHierarchyId: 'hierarchyId' }, observer);
});

it('waits for the Asset Hierarchy to become loaded', (done) => {
observable.subscribe((result) => {
// the worker returns the completed list of assets:
expect({
assetHierarchyId: 'hierarchyId',
assets: [sampleAssetSummary],
loadingState: LoadingStateEnum.LOADED,
}).toEqual(result);
done();
});
});
});

describe('Request the root assets', () => {
const mockDataSource = createMockSiteWiseAssetDataSource();
const mockListAssets = jest.fn();
const result: ListAssetsCommandOutput = { $metadata: {}, assetSummaries: [sampleAssetSummary] };
mockListAssets.mockReturnValue(Promise.resolve<ListAssetsCommandOutput>(result));
mockDataSource.listAssets = mockListAssets;
const requestProcessor: RequestProcessor = new RequestProcessor(mockDataSource, new SiteWiseAssetCache());

const observable: Observable<HierarchyAssetSummaryList> = new Observable<HierarchyAssetSummaryList>((observer) => {
requestProcessor.getAssetHierarchy({ assetHierarchyId: HIERARCHY_ROOT_ID }, observer);
});

it('waits for the root assets to become loaded', (done) => {
observable.subscribe((result) => {
// the worker returns the completed list of assets:
expect({
assetHierarchyId: HIERARCHY_ROOT_ID,
assets: [sampleAssetSummary],
loadingState: LoadingStateEnum.LOADED,
}).toEqual(result);
done();
});
});
});
Loading

0 comments on commit b9eabc2

Please sign in to comment.