feat: conditional schema based on column map instead of column field (#1978)
* feat: wip conditional schema based on column map instead of column field * feat: conditionalSchema columnMap and singular plural * fix: remove uuid fix * feat: add name and label (singular/plural) drop old tableColumnName
This commit is contained in:
@ -23,8 +23,8 @@ export class DataSourceMetadata {
|
||||
@Column({ type: 'enum', enum: ['postgres'], default: 'postgres' })
|
||||
type: DataSourceType;
|
||||
|
||||
@Column({ nullable: true, name: 'display_name' })
|
||||
displayName: string;
|
||||
@Column({ nullable: true, name: 'label' })
|
||||
label: string;
|
||||
|
||||
@Column({ default: false, name: 'is_remote' })
|
||||
isRemote: boolean;
|
||||
|
||||
@ -13,7 +13,22 @@ export class CreateFieldInput {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
displayName: string;
|
||||
nameSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
namePlural?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
labelSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
labelPlural?: string;
|
||||
|
||||
// Todo: use a type enum and share with typeorm entity
|
||||
@IsEnum([
|
||||
|
||||
@ -7,7 +7,22 @@ export class UpdateFieldInput {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayName: string;
|
||||
nameSingular?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
namePlural?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
labelSingular?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
labelPlural?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
||||
@ -50,11 +50,23 @@ export class FieldMetadata {
|
||||
type: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'display_name' })
|
||||
displayName: string;
|
||||
@Column({ nullable: false, name: 'name_singular' })
|
||||
nameSingular: string;
|
||||
|
||||
@Column({ nullable: false, name: 'target_column_name' })
|
||||
targetColumnName: string;
|
||||
@Field()
|
||||
@Column({ nullable: true, name: 'name_plural' })
|
||||
namePlural: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'label_singular' })
|
||||
labelSingular: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: true, name: 'label_plural' })
|
||||
labelPlural: string;
|
||||
|
||||
@Column({ nullable: false, name: 'target_column_map', type: 'jsonb' })
|
||||
targetColumnMap: FieldMetadataTargetColumnMap;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true, name: 'description', type: 'text' })
|
||||
@ -68,9 +80,6 @@ export class FieldMetadata {
|
||||
@Column({ nullable: true, name: 'placeholder' })
|
||||
placeholder: string;
|
||||
|
||||
@Column({ nullable: true, name: 'target_column_map', type: 'jsonb' })
|
||||
targetColumnMap: FieldMetadataTargetColumnMap;
|
||||
|
||||
@Column('text', { nullable: true, array: true })
|
||||
enums: string[];
|
||||
|
||||
|
||||
@ -11,17 +11,12 @@ import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
convertFieldMetadataToColumnChanges,
|
||||
convertMetadataTypeToColumnType,
|
||||
generateColumnName,
|
||||
generateTargetColumnMap,
|
||||
} from 'src/metadata/field-metadata/utils/field-metadata.util';
|
||||
import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service';
|
||||
import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service';
|
||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service';
|
||||
import {
|
||||
TenantMigrationColumnChange,
|
||||
TenantMigrationTableChange,
|
||||
} from 'src/metadata/tenant-migration/tenant-migration.entity';
|
||||
import { TenantMigrationTableChange } from 'src/metadata/tenant-migration/tenant-migration.entity';
|
||||
|
||||
@Injectable()
|
||||
export class FieldMetadataService extends TypeOrmQueryService<FieldMetadata> {
|
||||
@ -49,7 +44,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadata> {
|
||||
|
||||
const fieldAlreadyExists = await this.fieldMetadataRepository.findOne({
|
||||
where: {
|
||||
displayName: record.displayName,
|
||||
nameSingular: record.nameSingular,
|
||||
namePlural: record.namePlural,
|
||||
objectId: record.objectId,
|
||||
workspaceId: record.workspaceId,
|
||||
},
|
||||
@ -61,7 +57,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadata> {
|
||||
|
||||
const createdFieldMetadata = await super.createOne({
|
||||
...record,
|
||||
targetColumnName: generateColumnName(record.displayName), // deprecated
|
||||
targetColumnMap: generateTargetColumnMap(record.type),
|
||||
});
|
||||
|
||||
@ -69,15 +64,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadata> {
|
||||
{
|
||||
name: objectMetadata.targetTableName,
|
||||
change: 'alter',
|
||||
columns: [
|
||||
...convertFieldMetadataToColumnChanges(createdFieldMetadata),
|
||||
// Deprecated
|
||||
{
|
||||
name: createdFieldMetadata.targetColumnName,
|
||||
type: convertMetadataTypeToColumnType(record.type),
|
||||
change: 'create',
|
||||
} satisfies TenantMigrationColumnChange,
|
||||
],
|
||||
columns: convertFieldMetadataToColumnChanges(createdFieldMetadata),
|
||||
} satisfies TenantMigrationTableChange,
|
||||
]);
|
||||
|
||||
@ -87,13 +74,4 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadata> {
|
||||
|
||||
return createdFieldMetadata;
|
||||
}
|
||||
|
||||
public async getFieldMetadataByDisplayNameAndObjectId(
|
||||
name: string,
|
||||
objectId: string,
|
||||
): Promise<FieldMetadata | null> {
|
||||
return await this.fieldMetadataRepository.findOne({
|
||||
where: { displayName: name, objectId },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,17 +35,17 @@ export function generateTargetColumnMap(
|
||||
case 'boolean':
|
||||
case 'date':
|
||||
return {
|
||||
value: uuidToBase36(v4()),
|
||||
value: `column_${uuidToBase36(v4())}`,
|
||||
};
|
||||
case 'url':
|
||||
return {
|
||||
text: uuidToBase36(v4()),
|
||||
link: uuidToBase36(v4()),
|
||||
text: `column_${uuidToBase36(v4())}`,
|
||||
link: `column_${uuidToBase36(v4())}`,
|
||||
};
|
||||
case 'money':
|
||||
return {
|
||||
amount: uuidToBase36(v4()),
|
||||
currency: uuidToBase36(v4()),
|
||||
amount: `column_${uuidToBase36(v4())}`,
|
||||
currency: `column_${uuidToBase36(v4())}`,
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unknown type ${type}`);
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class MetadataNameLabelRefactoring1697126636202
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'MetadataNameLabelRefactoring1697126636202';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."data_source_metadata" RENAME COLUMN "display_name" TO "label"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "metadata"."tenant_migrations" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "migrations" jsonb, "applied_at" TIMESTAMP, "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_cb644cbc7f5092850f25eecb465" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_singular"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_plural"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "display_name"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "target_column_name"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "name_singular" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2" UNIQUE ("name_singular")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "name_plural" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1" UNIQUE ("name_plural")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "label_singular" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "label_plural" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "name_singular" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "name_plural" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "label_singular" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "label_plural" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "target_column_map" SET NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "target_column_map" DROP NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "id" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ALTER COLUMN "id" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "id" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_plural"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_singular"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_plural"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_singular"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "label_plural"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "label_singular"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "name_plural"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" DROP COLUMN "name_singular"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "target_column_name" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."field_metadata" ADD "display_name" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "display_name_plural" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "display_name_singular" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."object_metadata" ADD "display_name" character varying NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "metadata"."tenant_migrations"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."data_source_metadata" RENAME COLUMN "label" TO "display_name"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,21 +4,25 @@ import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class CreateObjectInput {
|
||||
// Deprecated
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
displayName: string;
|
||||
nameSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayNameSingular?: string;
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
namePlural: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayNamePlural?: string;
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
labelSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
labelPlural: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
||||
@ -4,21 +4,25 @@ import { IsBoolean, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class UpdateObjectInput {
|
||||
// Deprecated
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayName: string;
|
||||
@Field()
|
||||
nameSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayNameSingular?: string;
|
||||
@Field()
|
||||
namePlural: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
displayNamePlural?: string;
|
||||
@Field()
|
||||
labelSingular: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field()
|
||||
labelPlural: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
||||
@ -30,7 +30,7 @@ export class BeforeCreateOneObject<T extends ObjectMetadata>
|
||||
);
|
||||
|
||||
instance.input.dataSourceId = lastDataSourceMetadata.id;
|
||||
instance.input.targetTableName = instance.input.displayName;
|
||||
instance.input.targetTableName = instance.input.nameSingular;
|
||||
instance.input.workspaceId = workspaceId;
|
||||
instance.input.isActive = false;
|
||||
instance.input.isCustom = true;
|
||||
|
||||
@ -40,21 +40,25 @@ export class ObjectMetadata {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'data_source_id' })
|
||||
dataSourceId: string;
|
||||
|
||||
// Deprecated
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'display_name' })
|
||||
displayName: string;
|
||||
@Column({ nullable: false, name: 'name_singular', unique: true })
|
||||
nameSingular: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true, name: 'display_name_singular' })
|
||||
displayNameSingular: string;
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'name_plural', unique: true })
|
||||
namePlural: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true, name: 'display_name_plural' })
|
||||
displayNamePlural: string;
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'label_singular' })
|
||||
labelSingular: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: false, name: 'label_plural' })
|
||||
labelPlural: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true, name: 'description', type: 'text' })
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ConflictException, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
@ -22,17 +22,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadata> {
|
||||
}
|
||||
|
||||
override async createOne(record: ObjectMetadata): Promise<ObjectMetadata> {
|
||||
const objectAlreadyExists = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
displayName: record.displayName, // deprecated, use singular and plural
|
||||
workspaceId: record.workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (objectAlreadyExists) {
|
||||
throw new ConflictException('Object already exists');
|
||||
}
|
||||
|
||||
const createdObjectMetadata = await super.createOne(record);
|
||||
|
||||
await this.tenantMigrationService.createMigration(
|
||||
|
||||
Reference in New Issue
Block a user