in source/packages/services/assetlibrary/src/devices/devices.full.service.ts [377:517]
public async create(device: DeviceItem, applyProfile?: string): Promise<string> {
logger.debug(
`device.full.service create: in: device: ${JSON.stringify(
device
)}, applyProfile:${applyProfile}`
);
ow(device, ow.object.nonEmpty);
ow(device.templateId, ow.string.nonEmpty);
ow(device.deviceId, ow.string.nonEmpty);
// if a profile to apply has been provided, apply it first
if (applyProfile !== undefined) {
device = await this.applyProfile(device, applyProfile);
}
// remove any non printable characters from the id
device.deviceId = device.deviceId.replace(/[^\x20-\x7E]+/g, '');
// any ids need to be lowercase
this.setIdsToLowercase(device);
// default initial associations if none provided
if (
device.groups?.in === undefined &&
device.groups?.out === undefined &&
device.devices?.in === undefined &&
device.devices?.out === undefined &&
(this.defaultDeviceParentRelation ?? '') !== '' &&
(this.defaultDeviceParentGroup ?? '') !== ''
) {
device.groups = {
out: {
[this.defaultDeviceParentRelation]: [
{
id: this.defaultDeviceParentGroup,
},
],
},
};
}
// we can't check authz til here, as we need to understand any related devices and groups first
await this.authServiceFull.authorizationCheck(
device.listRelatedDeviceIds(),
device.listRelatedGroupPaths(),
ClaimAccess.C
);
// default initial state if none provided
if (device.state === undefined && this.defaultDeviceState !== undefined) {
device.state = <DeviceState>this.defaultDeviceState;
}
// perform validation of the device...
const template = await this.typesService.get(
device.templateId,
TypeCategory.Device,
TypeDefinitionStatus.published
);
if (template === undefined) {
throw new TemplateNotFoundError(device.templateId);
}
const validateSubTypeFuture = this.validator.validateSubType(
template,
device,
Operation.CREATE
);
const validateRelationshipsFuture = this.validator.validateRelationshipsByIds(
template,
device.groups,
device.devices
);
const [subTypeValidation, validateRelationships] = await Promise.all([
validateSubTypeFuture,
validateRelationshipsFuture,
]);
// schema validation results
if (!subTypeValidation.isValid) {
throw new SchemaValidationError(subTypeValidation.errors);
}
// validate the id associations
if (!validateRelationships.isValid) {
throw new RelationValidationError(validateRelationships);
}
// if fgac is enabled, we need to ensure any relations configured as identifying auth in its template are flagged to be saved as so
if (this.isAuthzEnabled) {
const incomingAuthRelations = template.schema.relations.incomingAuthRelations();
const outgoingAuthRelations = template.schema.relations.outgoingAuthRelations();
this.authServiceFull.updateRelsIdentifyingAuth(
device.groups?.in,
validateRelationships.groupLabels,
incomingAuthRelations
);
this.authServiceFull.updateRelsIdentifyingAuth(
device.groups?.out,
validateRelationships.groupLabels,
outgoingAuthRelations
);
this.authServiceFull.updateRelsIdentifyingAuth(
device.devices?.in,
validateRelationships.deviceLabels,
incomingAuthRelations
);
this.authServiceFull.updateRelsIdentifyingAuth(
device.devices?.out,
validateRelationships.deviceLabels,
outgoingAuthRelations
);
}
// Assemble devicemodel into node
device.category = TypeCategory.Device;
const node = this.devicesAssembler.toNode(device);
// Assemble the devices components
const components: Node[] = [];
if (device.components !== undefined) {
device.components.forEach((c) => {
c.category = TypeCategory.Component;
components.push(this.devicesAssembler.toNode(c));
});
}
// Save to datastore
const id = await this.devicesDao.create(node, device.groups, device.devices, components);
// fire event
await this.eventEmitter.fire({
objectId: device.deviceId,
type: Type.device,
event: Event.create,
payload: JSON.stringify(device),
});
logger.debug(`device.full.service create: exit: id: ${id}`);
return id;
}