public async attachToDevice()

in source/packages/services/assetlibrary/src/devices/devices.full.service.ts [948:1057]


    public async attachToDevice(
        deviceId: string,
        relationship: string,
        direction: RelationDirection,
        otherDeviceId: string
    ): Promise<void> {
        logger.debug(
            `device.full.service attachToDevice: in: deviceId:${deviceId}, relationship:${relationship}, direction:${direction}, otherDeviceId:${otherDeviceId}`
        );

        ow(deviceId, 'deviceId', ow.string.nonEmpty);
        ow(relationship, 'relationship', ow.string.nonEmpty);
        ow(direction, 'direction', ow.string.oneOf(['in', 'out']));
        ow(otherDeviceId, 'otherDeviceId', ow.string.nonEmpty);

        // any ids need to be lowercase
        deviceId = deviceId.toLowerCase();
        relationship = relationship.toLowerCase();
        otherDeviceId = otherDeviceId.toLowerCase();

        await this.authServiceFull.authorizationCheck(
            [deviceId, otherDeviceId],
            [],
            ClaimAccess.U
        );

        // fetch the existing device / group
        const deviceFuture = this.get(deviceId, false, [], false);
        const otherDeviceFuture = this.get(otherDeviceId, false, [], false);
        const [device, otherDevice] = await Promise.all([deviceFuture, otherDeviceFuture]);

        // make sure they exist
        if (device === undefined) {
            throw new DeviceNotFoundError(deviceId);
        }
        if (otherDevice === undefined) {
            throw new DeviceNotFoundError(otherDeviceId);
        }

        // if the relation already exists, there's no need to continue
        if (device.devices?.[direction]?.[relationship]?.find((e) => e.id === otherDeviceId)) {
            logger.debug(`device.full.service attachToDevice: relation already exits:`);
            return;
        }

        // ensure that the relation is allowed
        const relatedDevice: DirectionToRelatedEntityArrayMap = {
            [direction]: {
                [relationship]: [
                    {
                        id: otherDeviceId,
                    },
                ],
            },
        };
        const template = await this.typesService.get(
            device.templateId,
            TypeCategory.Device,
            TypeDefinitionStatus.published
        );
        if (template === undefined) {
            throw new TemplateNotFoundError(device.templateId);
        }
        const validateRelationships = await this.validator.validateRelationshipsByIds(
            template,
            undefined,
            relatedDevice
        );
        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
        let isAuthCheck = true;
        if (this.isAuthzEnabled) {
            const authRelations =
                direction === 'in'
                    ? template.schema.relations.incomingAuthRelations()
                    : template.schema.relations.outgoingAuthRelations();
            this.authServiceFull.updateRelsIdentifyingAuth(
                relatedDevice[direction],
                validateRelationships.deviceLabels,
                authRelations
            );
            isAuthCheck = relatedDevice[direction][relationship][0].isAuthCheck ?? false;
        }

        // Save to datastore
        await this.devicesDao.attachToDevice(
            deviceId,
            relationship,
            direction,
            otherDeviceId,
            isAuthCheck
        );

        // fire event
        await this.eventEmitter.fire({
            objectId: deviceId,
            type: Type.device,
            event: Event.modify,
            attributes: {
                deviceId,
                attachedToDevice: otherDeviceId,
                relationship,
            },
        });

        logger.debug(`device.full.service attachToDevice: exit:`);
    }