Add unique indexes and indexes for composite types (#7162)
Add support for indexes on composite fields and unicity constraint on indexes This pull request includes several changes across multiple files to improve error handling, enforce unique constraints, and update database migrations. The most important changes include updating error messages for snack bars, adding a new command to enforce unique constraints, and updating database migrations to include new fields and constraints. ### Error Handling Improvements: * [`packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx`](diffhunk://#diff-e7dc05ced8e4730430f5c7fcd0c75b3aa723da438c26e0bef8130b614427dd9aL23-R23): Updated error messages in `enqueueSnackBar` to use `error.message` directly. * [`packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts`](diffhunk://#diff-74c126d6bc7a5ed6b63be994d298df6669058034bfbc367b11045f9f31a3abe6L44-R46): Simplified error messages in `enqueueSnackBar`. * [`packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts`](diffhunk://#diff-af23a1d99639a66c251f87473e63e2b7bceaa4ee4f70fedfa0fcffe5c7d79181L56-R58): Simplified error messages in `enqueueSnackBar`. * [`packages/twenty-front/src/modules/object-record/hooks/useHandleFindManyRecordsError.ts`](diffhunk://#diff-da04296cbe280202a1eaf6b1244a30490d4f400411bee139651172c59719088eL22-R24): Simplified error messages in `enqueueSnackBar`. ### New Command for Unique Constraints: * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-enforce-unique-constraints.command.ts`](diffhunk://#diff-8337096c8c80dd2619a5ba691ae5145101f8ae0368a75192a050047e8c6ab7cbR1-R159): Added a new command to enforce unique constraints on company domain names and person emails. * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts`](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR13-R14): Integrated the new `EnforceUniqueConstraintsCommand` into the upgrade process. [[1]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR13-R14) [[2]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR31) [[3]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR64-R68) * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts`](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R7): Registered the new `EnforceUniqueConstraintsCommand` in the module. [[1]](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R7) [[2]](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R24) ### Database Migrations: * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726757368824-migrationDebt.ts`](diffhunk://#diff-c450aeae7bc0ef4416a0ade2dc613ca3f688629f35d2a32f90a09c3f494febdcR1-R53): Added a migration to update the `relationMetadata_ondeleteaction_enum` and set default values. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726757368825-addIsUniqueToIndexMetadata.ts`](diffhunk://#diff-8f1e14bd7f6835ec2c3bb39bcc51e3c318a3008d576a981e682f4c985e746fbfR1-R19): Added a migration to include the `isUnique` field in `indexMetadata`. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726762935841-addCompostiveColumnToIndexFieldMetadata.ts`](diffhunk://#diff-7c96b7276c7722d41ff31de23b2de4d6e09adfdc74815356ba63bc96a2669440R1-R19): Added a migration to include the `compositeColumn` field in `indexFieldMetadata`. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726766871572-addWhereToIndexMetadata.ts`](diffhunk://#diff-26651295a975eb50e672dce0e4e274e861f66feb1b68105eee5a04df32796190R1-R14): Added a migration to include the `indexWhereClause` field in `indexMetadata`. ### GraphQL Exception Handling: * [`packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts`](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R1-R4): Enhanced exception handling for `QueryFailedError` to provide more specific error messages for unique constraint violations. [[1]](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R1-R4) [[2]](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R23-R59) * [`packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts`](diffhunk://#diff-233d58ab2333586dd45e46e33d4f07e04a4b8adde4a11a48e25d86985e5a7943L58-R58): Updated the `workspaceQueryRunnerGraphqlApiExceptionHandler` call to include context. * [`packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts`](diffhunk://#diff-68b803f0762c407f5d2d1f5f8d389655a60654a2dd2394a81318655dcd44dc43L58-R58): Updated the `workspaceQueryRunnerGraphqlApiExceptionHandler` call to include context. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -10,6 +10,7 @@ export const emailsCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
isIncludedInUniqueConstraint: true,
|
||||
},
|
||||
{
|
||||
name: 'additionalEmails',
|
||||
|
||||
@ -10,12 +10,14 @@ export const fullNameCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
isIncludedInUniqueConstraint: true,
|
||||
},
|
||||
{
|
||||
name: 'lastName',
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
isIncludedInUniqueConstraint: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -16,6 +16,7 @@ export const linksCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
isIncludedInUniqueConstraint: true,
|
||||
},
|
||||
{
|
||||
name: 'secondaryLinks',
|
||||
|
||||
@ -10,6 +10,7 @@ export const phonesCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
isIncludedInUniqueConstraint: true,
|
||||
},
|
||||
{
|
||||
name: 'primaryPhoneCountryCode',
|
||||
|
||||
@ -118,6 +118,11 @@ export class FieldMetadataDTO<
|
||||
@Field({ nullable: true })
|
||||
isNullable?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
isUnique?: boolean;
|
||||
|
||||
@Validate(IsFieldMetadataDefaultValue)
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
|
||||
@ -108,6 +108,9 @@ export class FieldMetadataEntity<
|
||||
@Column({ nullable: true, default: true })
|
||||
isNullable: boolean;
|
||||
|
||||
@Column({ nullable: true, default: false })
|
||||
isUnique: boolean;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workspaceId: string;
|
||||
|
||||
@ -126,7 +129,7 @@ export class FieldMetadataEntity<
|
||||
@OneToMany(
|
||||
() => IndexFieldMetadataEntity,
|
||||
(indexFieldMetadata: IndexFieldMetadataEntity) =>
|
||||
indexFieldMetadata.fieldMetadata,
|
||||
indexFieldMetadata.indexMetadata,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
|
||||
@ -10,6 +10,7 @@ export interface CompositeProperty<
|
||||
type: Type;
|
||||
hidden: 'input' | 'output' | true | false;
|
||||
isRequired: boolean;
|
||||
isIncludedInUniqueConstraint?: boolean;
|
||||
isArray?: boolean;
|
||||
options?: FieldMetadataOptions<Type>;
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ export interface FieldMetadataInterface<
|
||||
workspaceId?: string;
|
||||
description?: string;
|
||||
isNullable?: boolean;
|
||||
isUnique?: boolean;
|
||||
fromRelationMetadata?: RelationMetadataEntity;
|
||||
toRelationMetadata?: RelationMetadataEntity;
|
||||
isCustom?: boolean;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { IndexMetadataInterface } from 'src/engine/metadata-modules/index-metadata/interfaces/index-metadata.interface';
|
||||
|
||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
||||
import { RelationMetadataInterface } from './relation-metadata.interface';
|
||||
|
||||
@ -13,6 +15,7 @@ export interface ObjectMetadataInterface {
|
||||
fromRelations: RelationMetadataInterface[];
|
||||
toRelations: RelationMetadataInterface[];
|
||||
fields: FieldMetadataInterface[];
|
||||
indexMetadatas: IndexMetadataInterface[];
|
||||
isSystem: boolean;
|
||||
isCustom: boolean;
|
||||
isActive: boolean;
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import { Field, HideField, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Authorize,
|
||||
FilterableField,
|
||||
IDField,
|
||||
QueryOptions,
|
||||
Relation,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import { IsDateString, IsNotEmpty, IsNumber, IsUUID } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
|
||||
import { IndexMetadataDTO } from './index-metadata.dto';
|
||||
|
||||
@ObjectType('indexField')
|
||||
@Authorize({
|
||||
authorize: (context: any) => ({
|
||||
workspaceId: { eq: context?.req?.workspace?.id },
|
||||
}),
|
||||
})
|
||||
@QueryOptions({
|
||||
defaultResultSize: 10,
|
||||
disableSort: true,
|
||||
maxResultsSize: 1000,
|
||||
})
|
||||
@Relation('indexMetadata', () => IndexMetadataDTO, {
|
||||
nullable: true,
|
||||
})
|
||||
@Relation('fieldMetadata', () => FieldMetadataDTO, {
|
||||
nullable: true,
|
||||
})
|
||||
export class IndexFieldMetadataDTO {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
indexMetadataId: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@FilterableField(() => UUIDScalarType)
|
||||
fieldMetadataId: string;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
order: number;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
|
||||
@HideField()
|
||||
workspaceId: string;
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
import {
|
||||
Field,
|
||||
HideField,
|
||||
ObjectType,
|
||||
registerEnumType,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Authorize,
|
||||
CursorConnection,
|
||||
FilterableField,
|
||||
IDField,
|
||||
QueryOptions,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
|
||||
import { IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
|
||||
registerEnumType(IndexType, {
|
||||
name: 'IndexType',
|
||||
description: 'Type of the index',
|
||||
});
|
||||
|
||||
@ObjectType('index')
|
||||
@Authorize({
|
||||
authorize: (context: any) => ({
|
||||
workspaceId: { eq: context?.req?.workspace?.id },
|
||||
}),
|
||||
})
|
||||
@QueryOptions({
|
||||
defaultResultSize: 10,
|
||||
disableSort: true,
|
||||
maxResultsSize: 1000,
|
||||
})
|
||||
@CursorConnection('objectMetadata', () => ObjectMetadataDTO)
|
||||
@CursorConnection('indexFieldMetadatas', () => IndexFieldMetadataDTO)
|
||||
export class IndexMetadataDTO {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
@IsValidMetadataName()
|
||||
name: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@FilterableField({ nullable: true })
|
||||
isCustom?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
isUnique: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
indexWhereClause?: string;
|
||||
|
||||
@IsEnum(IndexType)
|
||||
@IsNotEmpty()
|
||||
@Field(() => IndexType)
|
||||
indexType: IndexType;
|
||||
|
||||
objectMetadataId: string;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
|
||||
@HideField()
|
||||
workspaceId: string;
|
||||
}
|
||||
@ -23,6 +23,12 @@ export class IndexMetadataEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Column({ nullable: false })
|
||||
name: string;
|
||||
|
||||
@ -32,7 +38,7 @@ export class IndexMetadataEntity {
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
objectMetadataId: string;
|
||||
|
||||
@ManyToOne(() => ObjectMetadataEntity, (object) => object.indexes, {
|
||||
@ManyToOne(() => ObjectMetadataEntity, (object) => object.indexMetadatas, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
@ -48,15 +54,15 @@ export class IndexMetadataEntity {
|
||||
)
|
||||
indexFieldMetadatas: Relation<IndexFieldMetadataEntity[]>;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Column({ default: false })
|
||||
isCustom: boolean;
|
||||
|
||||
@Column({ nullable: false, default: false })
|
||||
isUnique: boolean;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
indexWhereClause: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: IndexType,
|
||||
|
||||
@ -1,14 +1,50 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { SortDirection } from '@ptc-org/nestjs-query-core';
|
||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service';
|
||||
import { ObjectMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/object-metadata/interceptors/object-metadata-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([IndexMetadataEntity], 'metadata'),
|
||||
WorkspaceMigrationModule,
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [
|
||||
NestjsQueryTypeOrmModule.forFeature(
|
||||
[IndexMetadataEntity, IndexFieldMetadataEntity],
|
||||
'metadata',
|
||||
),
|
||||
WorkspaceMigrationModule,
|
||||
],
|
||||
services: [IndexMetadataService],
|
||||
resolvers: [
|
||||
{
|
||||
EntityClass: IndexMetadataEntity,
|
||||
DTOClass: IndexMetadataDTO,
|
||||
read: {
|
||||
defaultSort: [{ field: 'id', direction: SortDirection.DESC }],
|
||||
many: {
|
||||
name: 'indexMetadatas', //TODO: check + singular
|
||||
},
|
||||
},
|
||||
create: {
|
||||
disabled: true,
|
||||
},
|
||||
update: { disabled: true },
|
||||
delete: { disabled: true },
|
||||
guards: [WorkspaceAuthGuard],
|
||||
interceptors: [ObjectMetadataGraphqlApiExceptionInterceptor],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
providers: [IndexMetadataService],
|
||||
exports: [IndexMetadataService],
|
||||
|
||||
@ -32,8 +32,10 @@ export class IndexMetadataService {
|
||||
workspaceId: string,
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
|
||||
isUnique: boolean,
|
||||
isCustom: boolean,
|
||||
indexType?: IndexType,
|
||||
indexWhereClause?: string,
|
||||
) {
|
||||
const tableName = computeObjectTargetTable(objectMetadata);
|
||||
|
||||
@ -82,6 +84,8 @@ export class IndexMetadataService {
|
||||
action: WorkspaceMigrationIndexActionType.CREATE,
|
||||
columns: columnNames,
|
||||
name: indexName,
|
||||
isUnique,
|
||||
where: indexWhereClause,
|
||||
type: indexType,
|
||||
},
|
||||
],
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { IndexMetadataInterface } from 'src/engine/metadata-modules/index-metadata/interfaces/index-metadata.interface';
|
||||
|
||||
export interface IndexFieldMetadataInterface {
|
||||
id: string;
|
||||
indexMetadataId: string;
|
||||
fieldMetadataId: string;
|
||||
fieldMetadata: FieldMetadataInterface;
|
||||
indexMetadata: IndexMetadataInterface;
|
||||
order: number;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { IndexFieldMetadataInterface } from 'src/engine/metadata-modules/index-metadata/interfaces/index-field-metadata.interface';
|
||||
|
||||
export interface IndexMetadataInterface {
|
||||
name: string;
|
||||
isUnique: boolean;
|
||||
indexFieldMetadatas: IndexFieldMetadataInterface[];
|
||||
}
|
||||
@ -11,6 +11,7 @@ import {
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { BeforeDeleteOneObject } from 'src/engine/metadata-modules/object-metadata/hooks/before-delete-one-object.hook';
|
||||
|
||||
@ObjectType('object')
|
||||
@ -26,6 +27,7 @@ import { BeforeDeleteOneObject } from 'src/engine/metadata-modules/object-metada
|
||||
})
|
||||
@BeforeDeleteOne(BeforeDeleteOneObject)
|
||||
@CursorConnection('fields', () => FieldMetadataDTO)
|
||||
@CursorConnection('indexMetadatas', () => IndexMetadataDTO)
|
||||
export class ObjectMetadataDTO {
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@ -86,7 +86,7 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
|
||||
@OneToMany(() => IndexMetadataEntity, (index) => index.objectMetadata, {
|
||||
cascade: true,
|
||||
})
|
||||
indexes: Relation<IndexMetadataEntity[]>;
|
||||
indexMetadatas: Relation<IndexMetadataEntity[]>;
|
||||
|
||||
@OneToMany(
|
||||
() => RelationMetadataEntity,
|
||||
|
||||
@ -673,6 +673,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
createdObjectMetadata,
|
||||
[searchVectorFieldMetadata],
|
||||
false,
|
||||
false,
|
||||
IndexType.GIN,
|
||||
);
|
||||
}
|
||||
|
||||
@ -154,6 +154,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
toObjectMetadata,
|
||||
[foreignKeyFieldMetadata, deletedFieldMetadata],
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
|
||||
@ -77,6 +77,8 @@ export class WorkspaceMetadataCacheService {
|
||||
'fields',
|
||||
'fields.fromRelationMetadata',
|
||||
'fields.toRelationMetadata',
|
||||
'indexMetadatas',
|
||||
'indexMetadatas.indexFieldMetadatas',
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
isArray: fieldMetadata.type === FieldMetadataType.ARRAY,
|
||||
isNullable: fieldMetadata.isNullable ?? true,
|
||||
isUnique: fieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
];
|
||||
@ -83,6 +84,7 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
isArray: currentFieldMetadata.type === FieldMetadataType.ARRAY,
|
||||
isNullable: currentFieldMetadata.isNullable ?? true,
|
||||
isUnique: currentFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue,
|
||||
),
|
||||
@ -92,6 +94,7 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
isArray: alteredFieldMetadata.type === FieldMetadataType.ARRAY,
|
||||
isNullable: alteredFieldMetadata.isNullable ?? true,
|
||||
isUnique: alteredFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
},
|
||||
|
||||
@ -69,6 +69,7 @@ export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<Co
|
||||
columnType: fieldMetadataTypeToColumnType(property.type),
|
||||
enum: enumOptions,
|
||||
isNullable: fieldMetadata.isNullable || !property.isRequired,
|
||||
isUnique: fieldMetadata.isUnique,
|
||||
defaultValue: serializedDefaultValue,
|
||||
isArray:
|
||||
property.type === FieldMetadataType.MULTI_SELECT || property.isArray,
|
||||
@ -168,6 +169,7 @@ export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<Co
|
||||
: undefined,
|
||||
isNullable:
|
||||
currentFieldMetadata.isNullable || !currentProperty.isRequired,
|
||||
isUnique: currentFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue?.[currentProperty.name],
|
||||
),
|
||||
@ -181,6 +183,7 @@ export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<Co
|
||||
enum: enumOptions,
|
||||
isNullable:
|
||||
alteredFieldMetadata.isNullable || !alteredProperty.isRequired,
|
||||
isUnique: alteredFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializedDefaultValue,
|
||||
isArray:
|
||||
alteredProperty.type === FieldMetadataType.MULTI_SELECT ||
|
||||
|
||||
@ -46,6 +46,7 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
enum: enumOptions,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: fieldMetadata.isNullable ?? true,
|
||||
isUnique: fieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
];
|
||||
@ -103,6 +104,7 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
: undefined,
|
||||
isArray: currentFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: currentFieldMetadata.isNullable ?? true,
|
||||
isUnique: currentFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue,
|
||||
),
|
||||
@ -113,6 +115,7 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
enum: enumOptions,
|
||||
isArray: alteredFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: alteredFieldMetadata.isNullable ?? true,
|
||||
isUnique: alteredFieldMetadata.isUnique ?? false,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
},
|
||||
|
||||
@ -32,6 +32,7 @@ export class TsVectorColumnActionFactory extends ColumnActionAbstractFactory<TsV
|
||||
columnName: computeColumnName(fieldMetadata),
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
isNullable: fieldMetadata.isNullable ?? true,
|
||||
isUnique: fieldMetadata.isUnique ?? false,
|
||||
defaultValue: undefined,
|
||||
generatedType: fieldMetadata.generatedType,
|
||||
asExpression: fieldMetadata.asExpression,
|
||||
|
||||
@ -30,6 +30,7 @@ export interface WorkspaceMigrationColumnDefinition {
|
||||
enum?: WorkspaceMigrationEnum[];
|
||||
isArray?: boolean;
|
||||
isNullable: boolean;
|
||||
isUnique?: boolean;
|
||||
defaultValue: any;
|
||||
generatedType?: 'STORED' | 'VIRTUAL';
|
||||
asExpression?: string;
|
||||
@ -39,6 +40,8 @@ export interface WorkspaceMigrationIndexAction {
|
||||
action: WorkspaceMigrationIndexActionType;
|
||||
name: string;
|
||||
columns: string[];
|
||||
isUnique: boolean;
|
||||
where?: string | null;
|
||||
type?: IndexType;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user