-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add label identifier to object decorator #6227
Changes from 12 commits
1969c0e
87fd98d
721fe99
3a316d7
7bb0a27
9ca022e
5ea0ef0
fab4b56
9bc7f02
f302e1c
c3ecff6
7d10239
e54515f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { MigrationInterface, QueryRunner } from 'typeorm'; | ||
|
||
export class FixIdentifierTypes1721057142509 implements MigrationInterface { | ||
name = 'FixIdentifierTypes1721057142509'; | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "labelIdentifierFieldMetadataId" TYPE uuid USING "labelIdentifierFieldMetadataId"::uuid`, | ||
); | ||
await queryRunner.query( | ||
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "imageIdentifierFieldMetadataId" TYPE uuid USING "imageIdentifierFieldMetadataId"::uuid`, | ||
); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "labelIdentifierFieldMetadataId" TYPE text USING "labelIdentifierFieldMetadataId"::text`, | ||
); | ||
await queryRunner.query( | ||
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "imageIdentifierFieldMetadataId" TYPE text USING "imageIdentifierFieldMetadataId"::text`, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
|
||
import { EntityManager, Repository } from 'typeorm'; | ||
|
||
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; | ||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface'; | ||
|
||
import { | ||
FieldMetadataEntity, | ||
FieldMetadataType, | ||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; | ||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; | ||
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory'; | ||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects'; | ||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage'; | ||
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util'; | ||
|
||
@Injectable() | ||
export class WorkspaceSyncObjectMetadataIdentifiersService { | ||
private readonly logger = new Logger( | ||
WorkspaceSyncObjectMetadataIdentifiersService.name, | ||
); | ||
|
||
constructor(private readonly standardObjectFactory: StandardObjectFactory) {} | ||
|
||
async synchronize( | ||
context: WorkspaceSyncContext, | ||
manager: EntityManager, | ||
_storage: WorkspaceSyncStorage, | ||
workspaceFeatureFlagsMap: FeatureFlagMap, | ||
): Promise<void> { | ||
const objectMetadataRepository = | ||
manager.getRepository(ObjectMetadataEntity); | ||
|
||
const originalObjectMetadataCollection = | ||
await this.getOriginalObjectMetadataCollection( | ||
context.workspaceId, | ||
objectMetadataRepository, | ||
); | ||
|
||
const standardObjectMetadataMap = this.createStandardObjectMetadataMap( | ||
context, | ||
workspaceFeatureFlagsMap, | ||
); | ||
|
||
await this.processObjectMetadataCollection( | ||
originalObjectMetadataCollection, | ||
standardObjectMetadataMap, | ||
objectMetadataRepository, | ||
); | ||
} | ||
|
||
private async getOriginalObjectMetadataCollection( | ||
workspaceId: string, | ||
objectMetadataRepository: Repository<ObjectMetadataEntity>, | ||
): Promise<ObjectMetadataEntity[]> { | ||
return await objectMetadataRepository.find({ | ||
where: { workspaceId, isCustom: false }, | ||
relations: ['fields'], | ||
}); | ||
} | ||
|
||
private createStandardObjectMetadataMap( | ||
context: WorkspaceSyncContext, | ||
workspaceFeatureFlagsMap: FeatureFlagMap, | ||
): Record<string, any> { | ||
const standardObjectMetadataCollection = this.standardObjectFactory.create( | ||
standardObjectMetadataDefinitions, | ||
context, | ||
workspaceFeatureFlagsMap, | ||
); | ||
|
||
return mapObjectMetadataByUniqueIdentifier( | ||
standardObjectMetadataCollection, | ||
); | ||
} | ||
|
||
private async processObjectMetadataCollection( | ||
originalObjectMetadataCollection: ObjectMetadataEntity[], | ||
standardObjectMetadataMap: Record<string, any>, | ||
objectMetadataRepository: Repository<ObjectMetadataEntity>, | ||
): Promise<void> { | ||
for (const objectMetadata of originalObjectMetadataCollection) { | ||
const objectStandardId = objectMetadata.standardId; | ||
|
||
if (!objectStandardId) { | ||
throw new Error( | ||
`Object ${objectMetadata.nameSingular} is missing standardId`, | ||
); | ||
} | ||
|
||
const labelIdentifierFieldMetadata = this.findIdentifierFieldMetadata( | ||
objectMetadata, | ||
objectStandardId, | ||
standardObjectMetadataMap, | ||
'labelIdentifierStandardId', | ||
); | ||
|
||
const imageIdentifierFieldMetadata = this.findIdentifierFieldMetadata( | ||
objectMetadata, | ||
objectStandardId, | ||
standardObjectMetadataMap, | ||
'imageIdentifierStandardId', | ||
); | ||
|
||
this.validateFieldMetadata( | ||
objectMetadata, | ||
labelIdentifierFieldMetadata, | ||
imageIdentifierFieldMetadata, | ||
); | ||
|
||
// TODO: Add image identifier field metadata | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you mind tackling this in this PR? I feel it's going to get lost otherwise There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what to do with them. I feel like except for people we don't really have any use case of imageIdentifier for standard objects. Also we don't have a file/image field type yet so I wanted to wait to implement properly. Wdyt? |
||
await objectMetadataRepository.save({ | ||
...objectMetadata, | ||
labelIdentifierFieldMetadataId: | ||
labelIdentifierFieldMetadata?.id ?? null, | ||
}); | ||
} | ||
} | ||
|
||
private findIdentifierFieldMetadata( | ||
objectMetadata: ObjectMetadataEntity, | ||
objectStandardId: string, | ||
standardObjectMetadataMap: Record<string, any>, | ||
standardIdFieldName: string, | ||
): FieldMetadataEntity | undefined { | ||
const identifierFieldMetadata = objectMetadata.fields.find( | ||
(field) => | ||
field.standardId === | ||
standardObjectMetadataMap[objectStandardId][standardIdFieldName], | ||
); | ||
|
||
if ( | ||
!identifierFieldMetadata && | ||
standardObjectMetadataMap[objectStandardId][standardIdFieldName] | ||
) { | ||
throw new Error( | ||
`Identifier field for object ${objectMetadata.nameSingular} does not exist`, | ||
); | ||
} | ||
|
||
return identifierFieldMetadata; | ||
} | ||
|
||
private validateFieldMetadata( | ||
objectMetadata: ObjectMetadataEntity, | ||
labelIdentifierFieldMetadata: FieldMetadataEntity | undefined, | ||
imageIdentifierFieldMetadata: FieldMetadataEntity | undefined, | ||
): void { | ||
if ( | ||
labelIdentifierFieldMetadata && | ||
![ | ||
FieldMetadataType.UUID, | ||
FieldMetadataType.TEXT, | ||
FieldMetadataType.FULL_NAME, | ||
].includes(labelIdentifierFieldMetadata.type) | ||
) { | ||
throw new Error( | ||
`Label identifier field for object ${objectMetadata.nameSingular} has invalid type ${labelIdentifierFieldMetadata.type}`, | ||
); | ||
} | ||
|
||
if (imageIdentifierFieldMetadata) { | ||
throw new Error( | ||
`Image identifier field for object ${objectMetadata.nameSingular} are not supported yet.`, | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed because nestjs/graphql usually infers the type from the property bellow but since now it can be either a string or null it does not know what to do so you have to specify the return type in the Field decorator