source/packages/services/assetlibrary-export/src/devices/devices.assembler.ts (345 lines of code) (raw):
/*********************************************************************************************************************
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
* with the License. A copy of the License is located at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
* and limitations under the License. *
*********************************************************************************************************************/
import { logger } from '@awssolutions/simple-cdf-logger';
import { inject, injectable } from 'inversify';
import { FullAssembler } from '../data/full.assembler';
import { Node, StringNodeMap } from '../data/node';
import { TYPES } from '../di/types';
import { TypeCategory } from '../types/constants';
import {
BulkDevicesResource,
Device10Resource,
Device20Resource,
DeviceBaseResource,
DeviceItem,
DeviceItemList,
DeviceResourceList,
DeviceState,
determineIfDevice20Resource,
} from './devices.models';
@injectable()
export class DevicesAssembler {
constructor(@inject(TYPES.FullAssembler) private fullAssembler: FullAssembler) {}
public toNode(model: DeviceItem): Node {
logger.silly(`device.assembler toNode: in: model:${JSON.stringify(model)}`);
const node = new Node();
node.types.push(model.category);
node.types.push(model.templateId);
node.attributes['deviceId'] = model.deviceId;
node.attributes['awsIotThingArn'] = model.awsIotThingArn;
node.attributes['description'] = model.description;
node.attributes['imageUrl'] = model.imageUrl;
node.attributes['connected'] = model.connected;
node.attributes['state'] = model.state;
node.version = model.version;
for (const p in model.attributes) {
if (model.attributes.hasOwnProperty(p)) {
node.attributes[p] = model.attributes[p];
}
}
logger.silly(`device.assembler toNode: exit: node: ${JSON.stringify(node)}`);
return node;
}
public toDeviceItems(nodes: Node[]): DeviceItem[] {
logger.silly(`device.assembler toDeviceItems: in: node: ${JSON.stringify(nodes)}`);
const devices: DeviceItem[] = [];
for (const node of nodes) {
devices.push(this.toDeviceItem(node));
}
return devices;
}
public toDeviceItem(node: Node): DeviceItem {
logger.silly(`device.assembler toDeviceItem: in: node: ${JSON.stringify(node)}`);
if (node === undefined) {
logger.silly(`device.assembler toDeviceItem: exit: model: undefined`);
return undefined;
}
const model = new DeviceItem();
if (node.types.indexOf(TypeCategory.Component) >= 0) {
model.category = TypeCategory.Component;
} else {
model.category = TypeCategory.Device;
}
model.templateId = node.types.filter(
(t) => t !== TypeCategory.Device && t !== TypeCategory.Component
)[0];
model.version = node.version;
Object.keys(node.attributes).forEach((key) => {
switch (key) {
case 'deviceId':
model.deviceId = <string>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
case 'awsIotThingArn':
model.awsIotThingArn = <string>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
case 'description':
model.description = <string>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
case 'imageUrl':
model.imageUrl = <string>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
case 'connected':
model.connected = <boolean>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
case 'state':
model.state = <DeviceState>(
this.fullAssembler.extractPropertyValue(node.attributes[key])
);
break;
default:
model.attributes[key] = this.fullAssembler.extractPropertyValue(
node.attributes[key]
);
}
});
this.assembleRelated(model, node.in, 'in');
this.assembleRelated(model, node.out, 'out');
// remove any empty collection attributes
if (model.groups && model.groups.in && Object.keys(model.groups.in).length === 0) {
delete model.groups.in;
}
if (model.groups && model.groups.out && Object.keys(model.groups.out).length === 0) {
delete model.groups.out;
}
if (Object.keys(model.groups).length === 0) {
delete model.groups;
}
if (model.devices && model.devices.in && Object.keys(model.devices.in).length === 0) {
delete model.devices.in;
}
if (model.devices && model.devices.out && Object.keys(model.devices.out).length === 0) {
delete model.devices.out;
}
if (Object.keys(model.devices).length === 0) {
delete model.devices;
}
if (model.components && model.components.length === 0) {
delete model.components;
}
logger.silly(`device.assembler toDeviceItem: exit: model: ${JSON.stringify(model)}`);
return model;
}
public fromDeviceResource(res: DeviceBaseResource): DeviceItem {
logger.silly(`device.assembler fromDeviceResource: in: res: ${JSON.stringify(res)}`);
if (res === undefined) {
logger.silly(`device.assembler fromDeviceResource: exit: res: undefined`);
return undefined;
}
const item = new DeviceItem();
// common properties
Object.keys(res).forEach((key) => {
if (key !== 'groups' && key !== 'devices') {
item[key] = res[key];
}
});
// populate version specific device info
if (determineIfDevice20Resource(res)) {
// v2.0 supports both incoming and outgoing links
const res_2_0 = res as Device20Resource;
item.groups = res_2_0.groups;
item.devices = res_2_0.devices;
} else {
// as v1.0 only supported outgoing links, we default all to outgoing
const res_1_0 = res as Device10Resource;
if (res_1_0.groups) {
item.groups = { out: {} };
Object.keys(res_1_0.groups).forEach(
(rel) => (item.groups.out[rel] = res_1_0.groups[rel])
);
}
if (res_1_0.devices) {
item.devices = { out: {} };
Object.keys(res_1_0.devices).forEach(
(rel) => (item.devices.out[rel] = res_1_0.devices[rel])
);
}
}
logger.silly(`device.assembler fromDeviceResource: exit: item: ${JSON.stringify(item)}`);
return item;
}
public fromBulkDevicesResource(res: BulkDevicesResource): DeviceItem[] {
logger.silly(`device.assembler fromBulkDevicesResource: in: res: ${JSON.stringify(res)}`);
if (res === undefined) {
logger.silly(`device.assembler fromBulkDevicesResource: exit: res: undefined`);
return undefined;
}
const items: DeviceItem[] = [];
res.devices.forEach((resource) => items.push(this.fromDeviceResource(resource)));
logger.silly(
`device.assembler fromBulkDevicesResource: exit: items: ${JSON.stringify(items)}`
);
return items;
}
public toDeviceResource(item: DeviceItem, version: string): DeviceBaseResource {
logger.silly(
`device.assembler toDeviceResource: in: item: ${JSON.stringify(
item
)}, version:${version}`
);
if (item === undefined) {
logger.silly(`device.assembler toDeviceResource: exit: item: undefined`);
return undefined;
}
let resource: DeviceBaseResource;
if (version.startsWith('1.')) {
// v1 specific...
resource = new Device10Resource();
const typedResource: Device10Resource = resource;
// populate version specific device info
if (item.groups) {
typedResource.groups = {};
if (item.groups.in) {
Object.keys(item.groups.in).forEach((rel) => {
typedResource.groups[rel] = item.groups.in[rel];
});
}
if (item.groups.out) {
Object.keys(item.groups.out).forEach((rel) => {
if (typedResource.groups[rel]) {
typedResource.groups[rel].push(...item.groups.out[rel]);
} else {
typedResource.groups[rel] = item.groups.out[rel];
}
});
}
} else {
delete typedResource.groups;
}
if (item.devices) {
typedResource.devices = {};
if (item.devices.in) {
Object.keys(item.devices.in).forEach((rel) => {
typedResource.devices[rel] = item.devices.in[rel];
});
}
if (item.devices.out) {
Object.keys(item.devices.out).forEach((rel) => {
if (typedResource.devices[rel]) {
typedResource.devices[rel].push(...item.devices.out[rel]);
} else {
typedResource.devices[rel] = item.devices.out[rel];
}
});
}
} else {
delete typedResource.devices;
}
} else {
// v2 specific...
resource = new Device20Resource();
const typedResource: Device20Resource = resource;
// populate version specific device info)
typedResource.groups = item.groups;
typedResource.devices = item.devices;
}
// common properties
Object.keys(item).forEach((key) => {
if (key !== 'groups' && key !== 'devices') {
resource[key] = item[key];
}
});
logger.silly(
`device.assembler toDeviceResource: exit: resource: ${JSON.stringify(resource)}`
);
return resource;
}
public toDeviceResourceList(items: DeviceItemList, version: string): DeviceResourceList {
logger.silly(
`device.assembler toDeviceResourceList: in: items: ${JSON.stringify(
items
)}, version:${version}`
);
if (items === undefined) {
logger.silly(`device.assembler toDeviceResourceList: exit: items: undefined`);
return undefined;
}
const resources = new DeviceResourceList();
resources.pagination = items.pagination;
resources.results = [];
items.results.forEach((item) =>
resources.results.push(this.toDeviceResource(item, version))
);
logger.silly(
`device.assembler toDeviceResourceList: exit: resources: ${JSON.stringify(resources)}`
);
return resources;
}
private assembleRelated(model: DeviceItem, related: StringNodeMap, direction: string) {
Object.keys(related).forEach((key) => {
const others = related[key];
if (others !== undefined) {
others.forEach((other) => {
if (other.category === TypeCategory.Group) {
if (model.groups[direction] === undefined) {
model.groups[direction] = {};
}
if (model.groups[direction][key] === undefined) {
model.groups[direction][key] = [];
}
model.groups[direction][key].push(
(other.attributes['groupPath'] as string[])[0]
);
} else if (other.category === TypeCategory.Device) {
if (model.devices[direction] === undefined) {
model.devices[direction] = {};
}
if (model.devices[direction][key] === undefined) {
model.devices[direction][key] = [];
}
model.devices[direction][key].push(
(other.attributes['deviceId'] as string[])[0]
);
} else if (other.category === TypeCategory.Component) {
if (model.components === undefined) {
model.components = [];
}
model.components.push(this.toDeviceItem(other));
}
});
}
});
}
public toRelatedDeviceModelsList(
node: Node,
offset?: number | string,
count?: number
): DeviceItemList {
logger.silly(
`devices.assembler toRelatedDeviceModelsList: in: node: ${JSON.stringify(node)}`
);
const r: DeviceItemList = {
results: [],
};
if (node === undefined) {
return r;
}
if (offset !== undefined || count !== undefined) {
r.pagination = {
offset,
count,
};
}
Object.keys(node.in).forEach((relationship) => {
const others = node.in[relationship];
if (others !== undefined) {
others.forEach((other) => {
const device: DeviceItem = this.toDeviceItem(other);
device.relation = relationship;
device.direction = 'in';
r.results.push(device);
});
}
});
Object.keys(node.out).forEach((relationship) => {
const others = node.out[relationship];
if (others !== undefined) {
others.forEach((other) => {
const device: DeviceItem = this.toDeviceItem(other);
device.relation = relationship;
device.direction = 'out';
r.results.push(device);
});
}
});
logger.silly(`groups.assembler toRelatedDeviceModelsList: exit: r: ${JSON.stringify(r)}`);
return r;
}
}