public async attachToGroup()

in source/packages/services/assetlibrary/src/devices/devices.full.service.ts [724:827]


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

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

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

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

        // fetch the existing device / group
        const deviceFuture = this.get(deviceId, false, [], true);
        const groupFuture = this.groupsService.get(groupPath, false);
        const [device, group] = await Promise.all([deviceFuture, groupFuture]);

        // make sure they exist
        if (device === undefined) {
            throw new DeviceNotFoundError(deviceId);
        }
        if (group === undefined) {
            throw new GroupNotFoundError(groupPath);
        }

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

        // ensure that the group relation is allowed
        const relatedGroup: DirectionToRelatedEntityArrayMap = {
            [direction]: {
                [relationship]: [
                    {
                        id: groupPath,
                    },
                ],
            },
        };

        const template = await this.typesService.get(
            device.templateId,
            TypeCategory.Device,
            TypeDefinitionStatus.published
        );
        const validateRelationships = await this.validator.validateRelationshipsByIds(
            template,
            relatedGroup,
            undefined
        );
        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(
                relatedGroup[direction],
                validateRelationships.groupLabels,
                authRelations
            );
            isAuthCheck = relatedGroup[direction][relationship][0].isAuthCheck ?? false;
        }

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

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

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