import { BadRequestException, Injectable, UnauthorizedException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { BeforeUpdateOneHook, UpdateOneInputType, } from '@ptc-org/nestjs-query-graphql'; import { Equal, In, Repository } from 'typeorm'; import { FieldMetadataEntity } from 'src/engine-metadata/field-metadata/field-metadata.entity'; import { UpdateObjectInput } from 'src/engine-metadata/object-metadata/dtos/update-object.input'; import { ObjectMetadataService } from 'src/engine-metadata/object-metadata/object-metadata.service'; import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity'; @Injectable() export class BeforeUpdateOneObject implements BeforeUpdateOneHook { constructor( readonly objectMetadataService: ObjectMetadataService, // TODO: Should not use the repository here @InjectRepository(FieldMetadataEntity, 'metadata') private readonly fieldMetadataRepository: Repository, ) {} // TODO: this logic could be moved to a policy guard async run( instance: UpdateOneInputType, context: any, ): Promise> { const workspaceId = context?.req?.user?.workspace?.id; if (!workspaceId) { throw new UnauthorizedException(); } const objectMetadata = await this.objectMetadataService.findOneWithinWorkspace(workspaceId, { where: { id: instance.id.toString(), }, }); if (!objectMetadata) { throw new BadRequestException('Object does not exist'); } if (!objectMetadata.isCustom) { if ( Object.keys(instance.update).length === 1 && instance.update.hasOwnProperty('isActive') && instance.update.isActive !== undefined ) { return { id: instance.id, update: { isActive: instance.update.isActive, } as T, }; } throw new BadRequestException( 'Only isActive field can be updated for standard objects', ); } if ( instance.update.labelIdentifierFieldMetadataId || instance.update.imageIdentifierFieldMetadataId ) { const fields = await this.fieldMetadataRepository.findBy({ workspaceId: Equal(workspaceId), objectMetadataId: Equal(instance.id.toString()), id: In( [ instance.update.labelIdentifierFieldMetadataId, instance.update.imageIdentifierFieldMetadataId, ].filter((id) => id !== null), ), }); const fieldIds = fields.map((field) => field.id); if ( instance.update.labelIdentifierFieldMetadataId && !fieldIds.includes(instance.update.labelIdentifierFieldMetadataId) ) { throw new BadRequestException('This label identifier does not exist'); } if ( instance.update.imageIdentifierFieldMetadataId && !fieldIds.includes(instance.update.imageIdentifierFieldMetadataId) ) { throw new BadRequestException('This image identifier does not exist'); } } this.checkIfFieldIsEditable(instance.update, objectMetadata); return instance; } // This is temporary until we properly use the MigrationRunner to update column names private checkIfFieldIsEditable( update: UpdateObjectInput, objectMetadata: ObjectMetadataEntity, ) { if ( update.nameSingular && update.nameSingular !== objectMetadata.nameSingular ) { throw new BadRequestException( "Object's nameSingular can't be updated. Please create a new object instead", ); } if ( update.labelSingular && update.labelSingular !== objectMetadata.labelSingular ) { throw new BadRequestException( "Object's labelSingular can't be updated. Please create a new object instead", ); } if (update.namePlural && update.namePlural !== objectMetadata.namePlural) { throw new BadRequestException( "Object's namePlural can't be updated. Please create a new object instead", ); } if ( update.labelPlural && update.labelPlural !== objectMetadata.labelPlural ) { throw new BadRequestException( "Object's labelPlural can't be updated. Please create a new object instead", ); } } }