Index v2 runner (#14537)
## Introduction https://github.com/twentyhq/twenty/pull/14363 In this PR we're creating builder and runner for index metadata Removing its previous integration within object builder ## Next: - implem index impact on field and object metadata api - Add integration testing coverage add integration test on index - Implement uniqueness toggle in field metadata api - add integration test on uniqueness related to https://github.com/twentyhq/core-team-issues/issues/1344
This commit is contained in:
+25
@@ -0,0 +1,25 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddUniversalIdentifierToIndexMetadata1758038863448
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddUniversalIdentifierToIndexMetadata1758038863448';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."indexMetadata" ADD "universalIdentifier" uuid`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "IDX_b27c681286ac581f81498c5d4b" ON "core"."indexMetadata" ("workspaceId", "universalIdentifier") `,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_b27c681286ac581f81498c5d4b"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."indexMetadata" DROP COLUMN "universalIdentifier"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -4,4 +4,5 @@ export const ALL_FLAT_ENTITY_MAPS_PROPERTIES = [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewMaps',
|
||||
'flatIndexMaps',
|
||||
] as const satisfies (keyof AllFlatEntityMaps)[];
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ export const EMPTY_ALL_FLAT_ENTITY_MAPS = {
|
||||
...EMPTY_FLAT_ENTITY_MAPS,
|
||||
idByNameSingular: {},
|
||||
},
|
||||
flatIndexMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
flatViewFieldMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
flatViewMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
} as const satisfies AllFlatEntityMaps;
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type AllFlatEntitiesByMetadataEngineName = {
|
||||
@@ -7,4 +8,5 @@ export type AllFlatEntitiesByMetadataEngineName = {
|
||||
objectMetadata: FlatObjectMetadata;
|
||||
view: FlatView;
|
||||
viewField: FlatViewField;
|
||||
index: FlatIndexMetadata;
|
||||
};
|
||||
|
||||
+10
-9
@@ -1,13 +1,14 @@
|
||||
import { type FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities-by-metadata-engine-name.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
export type AllFlatEntityMaps = {
|
||||
export type AllFlatEntityMaps = Omit<
|
||||
{
|
||||
[P in keyof AllFlatEntitiesByMetadataEngineName as `flat${Capitalize<P>}Maps`]: FlatEntityMaps<
|
||||
AllFlatEntitiesByMetadataEngineName[P]
|
||||
>;
|
||||
},
|
||||
'flatObjectMetadataMaps'
|
||||
> & {
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
flatViewFieldMaps: FlatViewFieldMaps;
|
||||
};
|
||||
|
||||
// export type AllFlatEntityMaps = {
|
||||
// [P in keyof AllFlatEntities as `${P}Maps`]: FlatEntityMaps<AllFlatEntities[P]>;
|
||||
// };
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ export const throwOnFieldInputTranspilationsError: ThrowOnFieldInputTranspilatio
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
{
|
||||
report: {
|
||||
index: [],
|
||||
view: [],
|
||||
viewField: [],
|
||||
objectMetadata: [
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { type FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||
|
||||
type FlatIndexFieldMetadataOverrides = Required<
|
||||
Pick<
|
||||
FlatIndexFieldMetadata,
|
||||
'fieldMetadataId' | 'indexMetadataId' | 'universalIdentifier'
|
||||
>
|
||||
> &
|
||||
Partial<FlatIndexFieldMetadata>;
|
||||
export const getFlatIndexFieldMetadataMock = (
|
||||
overrides: FlatIndexFieldMetadataOverrides,
|
||||
): FlatIndexFieldMetadata => {
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
createdAt,
|
||||
id: faker.string.uuid(),
|
||||
order: faker.number.int(),
|
||||
updatedAt: createdAt,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -17,7 +17,7 @@ export const getFlatIndexMetadataMock = (
|
||||
createdAt,
|
||||
id: faker.string.uuid(),
|
||||
indexType: IndexType.BTREE,
|
||||
indexWhereClause: undefined,
|
||||
indexWhereClause: null,
|
||||
isCustom: false,
|
||||
isUnique: false,
|
||||
name: 'defaultFlatIndexMetadataName',
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
|
||||
export const FLAT_INDEX_METADATA_PROPERTIES_TO_COMPARE = [
|
||||
'indexType',
|
||||
'indexWhereClause',
|
||||
'flatIndexFieldMetadatas',
|
||||
'isUnique',
|
||||
'name',
|
||||
] as const satisfies (keyof FlatIndexMetadata)[];
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type IndexMetadataRelationProperties } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
|
||||
export const INDEX_METADATA_ENTITY_RELATION_PROPERTIES = [
|
||||
'objectMetadata',
|
||||
'indexFieldMetadatas',
|
||||
] as const satisfies readonly IndexMetadataRelationProperties[];
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
export enum IndexExceptionCode {
|
||||
INDEX_EMPTY_FIELDS = 'INDEX_EMPTY_FIELDS',
|
||||
INDEX_FIELD_NOT_FOUND = 'INDEX_FIELD_NOT_FOUND',
|
||||
INDEX_FIELD_WRONG_OBJECT = 'INDEX_FIELD_WRONG_OBJECT',
|
||||
INDEX_FIELD_INVALID_REFERENCE = 'INDEX_FIELD_INVALID_REFERENCE',
|
||||
INDEX_FIELD_ID_DUPLICATE = 'INDEX_FIELD_ID_DUPLICATE',
|
||||
INDEX_ALREADY_EXISTS = 'INDEX_ALREADY_EXISTS',
|
||||
INDEX_NOT_FOUND = 'INDEX_NOT_FOUND',
|
||||
INDEX_OBJECT_NOT_FOUND = 'INDEX_OBJECT_NOT_FOUND',
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { fromIndexMetadataEntityToFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatIndexMaps')
|
||||
export class WorkspaceFlatIndexMapCacheService extends WorkspaceFlatMapCacheService<
|
||||
FlatEntityMaps<FlatIndexMetadata>
|
||||
> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
@InjectRepository(IndexMetadataEntity)
|
||||
private readonly indexMetadataRepository: Repository<IndexMetadataEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatEntityMaps<FlatIndexMetadata>> {
|
||||
const indexes = await this.indexMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: ['indexFieldMetadatas'],
|
||||
});
|
||||
|
||||
const flatIndexMaps = indexes.reduce<FlatEntityMaps<FlatIndexMetadata>>(
|
||||
(flatEntityMaps, index) => {
|
||||
const flatIndex = fromIndexMetadataEntityToFlatIndexMetadata(index);
|
||||
|
||||
return {
|
||||
byId: {
|
||||
...flatEntityMaps.byId,
|
||||
[flatIndex.id]: flatIndex,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
...flatEntityMaps.idByUniversalIdentifier,
|
||||
[flatIndex.universalIdentifier]: flatIndex.id,
|
||||
},
|
||||
};
|
||||
},
|
||||
EMPTY_FLAT_ENTITY_MAPS,
|
||||
);
|
||||
|
||||
return flatIndexMaps;
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { type MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type IndexFieldMetadataEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
IndexFieldMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatIndexFieldMetadata = Partial<
|
||||
Omit<IndexFieldMetadataEntity, IndexFieldMetadataEntityRelationProperties>
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FLAT_INDEX_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-index-metadata/constants/flat-index-metadata-properties-to-compare.constant';
|
||||
|
||||
export type FlatIndexMetadataPropertiesToCompare =
|
||||
(typeof FLAT_INDEX_METADATA_PROPERTIES_TO_COMPARE)[number];
|
||||
+16
-10
@@ -1,18 +1,24 @@
|
||||
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { type FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||
import { type MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type IndexMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||
IndexMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
export type IndexMetadataRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
IndexMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
// TODO prastoin refactor FlatIndexMetadata to not be a Partial extension of IndexMetadataEntity
|
||||
export type FlatIndexMetadata = Partial<
|
||||
Omit<IndexMetadataEntity, IndexMetadataRelationProperties>
|
||||
export type FlatIndexMetadata = Omit<
|
||||
IndexMetadataEntity,
|
||||
IndexMetadataRelationProperties
|
||||
> & {
|
||||
id: string;
|
||||
flatIndexFieldMetadatas: FlatIndexFieldMetadata[];
|
||||
universalIdentifier: string;
|
||||
flatIndexFieldMetadatas: Omit<
|
||||
IndexFieldMetadataEntity,
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
IndexFieldMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>
|
||||
>[];
|
||||
};
|
||||
|
||||
+24
-26
@@ -1,37 +1,32 @@
|
||||
import diff from 'microdiff';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { parseJson } from 'twenty-shared/utils';
|
||||
|
||||
import { FLAT_INDEX_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-index-metadata/constants/flat-index-metadata-properties-to-compare.constant';
|
||||
import { type FlatIndexMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata-properties-to-compare.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const flatIndexMetadataPropertiesToCompare = [
|
||||
'flatIndexFieldMetadatas', // Comparing this as whole ? should iterate on each keys ? => TBD should only map over cols as before ?
|
||||
'indexType',
|
||||
'indexWhereClause',
|
||||
'isUnique',
|
||||
'name',
|
||||
] as const satisfies (keyof FlatIndexMetadata)[];
|
||||
|
||||
type FlatIndexMetadataPropertiesToCompare =
|
||||
(typeof flatIndexMetadataPropertiesToCompare)[number];
|
||||
|
||||
// Should also handle indexFieldMetadata comparison ?
|
||||
/**
|
||||
* This comparator handles update on colliding universalIdentifier flatIndexMetadata
|
||||
*/
|
||||
export const compareTwoFlatIndexMetadata = ({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<FlatIndexMetadata>) => {
|
||||
fromFlatIndexMetadata,
|
||||
toFlatIndexMetadata,
|
||||
}: FromTo<FlatIndexMetadata, 'flatIndexMetadata'>) => {
|
||||
const transformOptions = {
|
||||
propertiesToStringify: ['flatIndexFieldMetadatas'] as const,
|
||||
shouldIgnoreProperty: (property: string) =>
|
||||
!flatIndexMetadataPropertiesToCompare.includes(
|
||||
!FLAT_INDEX_METADATA_PROPERTIES_TO_COMPARE.includes(
|
||||
property as FlatIndexMetadataPropertiesToCompare,
|
||||
),
|
||||
};
|
||||
|
||||
const fromCompare = transformMetadataForComparison(from, transformOptions);
|
||||
const toCompare = transformMetadataForComparison(to, transformOptions);
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
fromFlatIndexMetadata,
|
||||
transformOptions,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
toFlatIndexMetadata,
|
||||
transformOptions,
|
||||
);
|
||||
|
||||
const flatIndexeDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
@@ -40,17 +35,20 @@ export const compareTwoFlatIndexMetadata = ({
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatIndexMetadataPropertiesToCompare;
|
||||
|
||||
const property = path[0];
|
||||
|
||||
if (typeof property === 'number') {
|
||||
return [];
|
||||
if (property === 'flatIndexFieldMetadatas') {
|
||||
return {
|
||||
from: parseJson(oldValue),
|
||||
to: parseJson(value),
|
||||
property,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
property,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { INDEX_METADATA_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-index-metadata/constants/index-metadata-entity-relation-properties.constant';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
|
||||
export const fromIndexMetadataEntityToFlatIndexMetadata = (
|
||||
indexMetadataEntity: IndexMetadataEntity,
|
||||
): FlatIndexMetadata => {
|
||||
const indexMetadataEntityWithoutRelations = removePropertiesFromRecord(
|
||||
indexMetadataEntity,
|
||||
[...INDEX_METADATA_ENTITY_RELATION_PROPERTIES],
|
||||
);
|
||||
|
||||
return {
|
||||
...indexMetadataEntityWithoutRelations,
|
||||
universalIdentifier:
|
||||
indexMetadataEntityWithoutRelations.universalIdentifier ??
|
||||
indexMetadataEntityWithoutRelations.id,
|
||||
flatIndexFieldMetadatas: indexMetadataEntity.indexFieldMetadatas.map(
|
||||
(indexFieldMetadata) =>
|
||||
removePropertiesFromRecord(indexFieldMetadata, [
|
||||
'indexMetadata',
|
||||
'fieldMetadata',
|
||||
]),
|
||||
),
|
||||
};
|
||||
};
|
||||
+3
-1
@@ -14,7 +14,9 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
|
||||
@Entity('indexFieldMetadata')
|
||||
export class IndexFieldMetadataEntity {
|
||||
export class IndexFieldMetadataEntity
|
||||
implements Required<IndexFieldMetadataEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
||||
+6
-1
@@ -12,6 +12,8 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/interfaces/syncable-entity.interface';
|
||||
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -26,7 +28,10 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
'objectMetadataId',
|
||||
])
|
||||
@Entity('indexMetadata')
|
||||
export class IndexMetadataEntity {
|
||||
export class IndexMetadataEntity
|
||||
extends SyncableEntity
|
||||
implements Required<IndexMetadataEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
||||
+27
-111
@@ -15,48 +15,34 @@ export class WorkspaceSchemaIndexManagerService {
|
||||
tableName: string;
|
||||
index: WorkspaceSchemaIndexDefinition;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeTableName = removeSqlDDLInjection(tableName);
|
||||
const safeIndexName = removeSqlDDLInjection(index.name);
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeTableName = removeSqlDDLInjection(tableName);
|
||||
const safeIndexName = removeSqlDDLInjection(index.name);
|
||||
|
||||
const quotedColumns = index.columns.map(
|
||||
(column) => `"${removeSqlDDLInjection(column)}"`,
|
||||
);
|
||||
const isUnique = index.isUnique ? 'UNIQUE' : '';
|
||||
const indexType =
|
||||
index.type && index.type !== 'BTREE' ? `USING ${index.type}` : '';
|
||||
const whereClause = index.where ? `WHERE ${index.where}` : ''; // TODO: to sanitize
|
||||
const includeClause = index.include?.length
|
||||
? `INCLUDE (${index.include
|
||||
.map((col) => `"${removeSqlDDLInjection(col)}"`)
|
||||
.join(', ')})`
|
||||
: '';
|
||||
const quotedColumns = index.columns.map(
|
||||
(column) => `"${removeSqlDDLInjection(column)}"`,
|
||||
);
|
||||
const isUnique = index.isUnique ? 'UNIQUE' : '';
|
||||
const indexType =
|
||||
index.type && index.type !== 'BTREE' ? `USING ${index.type}` : '';
|
||||
const whereClause = index.where ? `WHERE ${index.where}` : ''; // TODO: to sanitize -> might search for a lib to sanitize sql queries
|
||||
|
||||
const sql = [
|
||||
'CREATE',
|
||||
isUnique && 'UNIQUE',
|
||||
'INDEX IF NOT EXISTS',
|
||||
`"${safeIndexName}"`,
|
||||
'ON',
|
||||
`"${safeSchemaName}"."${safeTableName}"`,
|
||||
indexType,
|
||||
`(${quotedColumns.join(', ')})`,
|
||||
includeClause,
|
||||
whereClause,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.trim();
|
||||
const sql = [
|
||||
'CREATE',
|
||||
isUnique && 'UNIQUE',
|
||||
'INDEX IF NOT EXISTS',
|
||||
`"${safeIndexName}"`,
|
||||
'ON',
|
||||
`"${safeSchemaName}"."${safeTableName}"`,
|
||||
indexType,
|
||||
`(${quotedColumns.join(', ')})`,
|
||||
whereClause,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
await queryRunner.query(sql);
|
||||
} catch (error: unknown) {
|
||||
// Ignore error if index already exists
|
||||
if (error instanceof Error && 'code' in error && error.code === '42P07') {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
await queryRunner.query(sql);
|
||||
}
|
||||
|
||||
async dropIndex({
|
||||
@@ -67,80 +53,10 @@ export class WorkspaceSchemaIndexManagerService {
|
||||
queryRunner: QueryRunner;
|
||||
schemaName: string;
|
||||
indexName: string;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeIndexName = removeSqlDDLInjection(indexName);
|
||||
const sql = `DROP INDEX IF EXISTS "${safeSchemaName}"."${safeIndexName}"`;
|
||||
|
||||
await queryRunner.query(sql);
|
||||
} catch (error: unknown) {
|
||||
// Ignore error if index does not exist
|
||||
if (error instanceof Error && 'code' in error && error.code === '42704') {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async renameIndex({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
oldIndexName,
|
||||
newIndexName,
|
||||
}: {
|
||||
queryRunner: QueryRunner;
|
||||
schemaName: string;
|
||||
oldIndexName: string;
|
||||
newIndexName: string;
|
||||
}): Promise<void> {
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeOldIndexName = removeSqlDDLInjection(oldIndexName);
|
||||
const safeNewIndexName = removeSqlDDLInjection(newIndexName);
|
||||
const sql = `ALTER INDEX "${safeSchemaName}"."${safeOldIndexName}" RENAME TO "${safeNewIndexName}"`;
|
||||
|
||||
await queryRunner.query(sql);
|
||||
}
|
||||
|
||||
async createUniqueConstraint({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
constraintName,
|
||||
columnNames,
|
||||
}: {
|
||||
queryRunner: QueryRunner;
|
||||
schemaName: string;
|
||||
tableName: string;
|
||||
constraintName: string;
|
||||
columnNames: string[];
|
||||
}): Promise<void> {
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeTableName = removeSqlDDLInjection(tableName);
|
||||
const safeConstraintName = removeSqlDDLInjection(constraintName);
|
||||
const quotedColumns = columnNames
|
||||
.map((col) => `"${removeSqlDDLInjection(col)}"`)
|
||||
.join(', ');
|
||||
const sql = `ALTER TABLE "${safeSchemaName}"."${safeTableName}" ADD CONSTRAINT "${safeConstraintName}" UNIQUE (${quotedColumns})`;
|
||||
|
||||
await queryRunner.query(sql);
|
||||
}
|
||||
|
||||
async dropUniqueConstraint({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
constraintName,
|
||||
}: {
|
||||
queryRunner: QueryRunner;
|
||||
schemaName: string;
|
||||
tableName: string;
|
||||
constraintName: string;
|
||||
}): Promise<void> {
|
||||
const safeSchemaName = removeSqlDDLInjection(schemaName);
|
||||
const safeTableName = removeSqlDDLInjection(tableName);
|
||||
const safeConstraintName = removeSqlDDLInjection(constraintName);
|
||||
const sql = `ALTER TABLE "${safeSchemaName}"."${safeTableName}" DROP CONSTRAINT IF EXISTS "${safeConstraintName}"`;
|
||||
const safeIndexName = removeSqlDDLInjection(indexName);
|
||||
const sql = `DROP INDEX IF EXISTS "${safeSchemaName}"."${safeIndexName}"`;
|
||||
|
||||
await queryRunner.query(sql);
|
||||
}
|
||||
|
||||
-1
@@ -12,5 +12,4 @@ export type WorkspaceSchemaIndexDefinition = {
|
||||
type?: WorkspaceSchemaIndexType;
|
||||
isUnique?: boolean;
|
||||
where?: string;
|
||||
include?: string[];
|
||||
};
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ export type ValidationErrorResponse = {
|
||||
summary: {
|
||||
totalErrors: number;
|
||||
} & {
|
||||
[P in keyof AllFlatEntitiesByMetadataEngineName as `invalid${Capitalize<P>}s`]: number;
|
||||
[P in keyof AllFlatEntitiesByMetadataEngineName as `invalid${Capitalize<P>}`]: number;
|
||||
};
|
||||
errors: OrchestratorFailureReport;
|
||||
};
|
||||
|
||||
+5
-3
@@ -6,12 +6,14 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
|
||||
): ValidationErrorResponse => {
|
||||
const emptyResponseError: ValidationErrorResponse = {
|
||||
summary: {
|
||||
invalidObjectMetadatas: 0,
|
||||
invalidViews: 0,
|
||||
invalidViewFields: 0,
|
||||
invalidObjectMetadata: 0,
|
||||
invalidView: 0,
|
||||
invalidViewField: 0,
|
||||
invalidIndex: 0,
|
||||
totalErrors: 0,
|
||||
},
|
||||
errors: {
|
||||
index: [],
|
||||
objectMetadata: [],
|
||||
view: [],
|
||||
viewField: [],
|
||||
|
||||
+2
-5
@@ -13,14 +13,11 @@ export const workspaceMigrationBuilderExceptionV2Formatter = (
|
||||
const { errors, summary } =
|
||||
fromWorkspaceMigrationBuilderExceptionToValidationResponseError(error);
|
||||
|
||||
const invalidObjects = summary.invalidObjectMetadatas;
|
||||
const invalidFields = summary.invalidViewFields;
|
||||
|
||||
throw new BaseGraphQLError(error.message, ErrorCode.BAD_USER_INPUT, {
|
||||
code: 'METADATA_VALIDATION_ERROR',
|
||||
errors,
|
||||
summary,
|
||||
message: `Validation failed for ${invalidObjects} object(s) and ${invalidFields} field(s)`,
|
||||
userFriendlyMessage: t`Validation failed for ${invalidObjects} object(s) and ${invalidFields} field(s)`,
|
||||
message: `Validation failed for 0 object(s) and 0 field(s)`,
|
||||
userFriendlyMessage: t`Validation failed for 0 object(s) and 0 field(s)`,
|
||||
});
|
||||
};
|
||||
|
||||
+34
-3
@@ -10,6 +10,7 @@ import {
|
||||
WorkspaceMigrationOrchestratorFailedResult,
|
||||
WorkspaceMigrationOrchestratorSuccessfulResult,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
@@ -23,11 +24,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
|
||||
constructor(
|
||||
private readonly workspaceMigrationBuilderV2Service: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceMigrationV2IndexActionsBuilderService: WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
) {}
|
||||
|
||||
// This does not handle dependency maps correctly
|
||||
private setupOptimisticCache({
|
||||
fromToAllFlatEntityMaps,
|
||||
dependencyAllFlatEntityMaps,
|
||||
@@ -73,14 +74,19 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
objectMetadata: [],
|
||||
view: [],
|
||||
viewField: [],
|
||||
index: [],
|
||||
};
|
||||
|
||||
const optimisticAllFlatEntityMaps = this.setupOptimisticCache({
|
||||
fromToAllFlatEntityMaps,
|
||||
dependencyAllFlatEntityMaps,
|
||||
});
|
||||
const { flatObjectMetadataMaps, flatViewFieldMaps, flatViewMaps } =
|
||||
fromToAllFlatEntityMaps;
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
flatIndexMaps,
|
||||
} = fromToAllFlatEntityMaps;
|
||||
|
||||
if (isDefined(flatObjectMetadataMaps)) {
|
||||
const { from: fromFlatObjectMetadataMaps, to: toFlatObjectMetadataMaps } =
|
||||
@@ -104,6 +110,31 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatIndexMaps)) {
|
||||
const { from: fromFlatIndexMaps, to: toFlatIndexMaps } = flatIndexMaps;
|
||||
const indexResult =
|
||||
await this.workspaceMigrationV2IndexActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
from: fromFlatIndexMaps,
|
||||
to: toFlatIndexMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatIndexMaps =
|
||||
indexResult.optimisticFlatEntityMaps;
|
||||
|
||||
if (indexResult.status === 'fail') {
|
||||
orchestratorFailureReport.index.push(...indexResult.errors);
|
||||
} else {
|
||||
allActions.push(...indexResult.actions);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatViewMaps)) {
|
||||
const { from: fromFlatViewMaps, to: toFlatViewMaps } = flatViewMaps;
|
||||
const viewResult =
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { type MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type IndexFieldMetadataEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
IndexFieldMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatIndexFieldMetadata = Partial<
|
||||
Omit<IndexFieldMetadataEntity, IndexFieldMetadataEntityRelationProperties>
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
export type PropertyUpdate<T, P extends keyof T> = {
|
||||
property: P;
|
||||
} & FromTo<T[P]>;
|
||||
+1
-60
@@ -342,65 +342,6 @@ exports[`Workspace migration builder field actions test suite It should build an
|
||||
|
||||
exports[`Workspace migration builder field actions test suite It should not infer any actions as from and to fields are identical 1`] = `[]`;
|
||||
|
||||
exports[`Workspace migration builder index actions test suite It should build a delete_index action 1`] = `
|
||||
[
|
||||
{
|
||||
"flatIndexMetadataId": Any<String>,
|
||||
"type": "delete_index",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Workspace migration builder index actions test suite It should build an create_index action 1`] = `
|
||||
[
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"createdAt": Any<ClockDate>,
|
||||
"flatIndexFieldMetadatas": [],
|
||||
"id": Any<String>,
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": undefined,
|
||||
"isCustom": false,
|
||||
"isUnique": false,
|
||||
"name": "defaultFlatIndexMetadataName",
|
||||
"objectMetadataId": Any<String>,
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Workspace migration builder index actions test suite It should build an delete_index and a create_index action ( the way we handle update ) 1`] = `
|
||||
[
|
||||
{
|
||||
"flatIndexMetadataId": Any<String>,
|
||||
"type": "delete_index",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"createdAt": Any<ClockDate>,
|
||||
"flatIndexFieldMetadatas": [],
|
||||
"id": Any<String>,
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": "new index where clause",
|
||||
"isCustom": false,
|
||||
"isUnique": false,
|
||||
"name": "new index name",
|
||||
"objectMetadataId": Any<String>,
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Workspace migration builder index actions test suite It should not infer any actions as from and to indexes are identical 1`] = `[]`;
|
||||
|
||||
exports[`Workspace migration builder object actions test suite It should build a create_object action with custom object 1`] = `
|
||||
[
|
||||
{
|
||||
@@ -2981,7 +2922,7 @@ exports[`Workspace migration builder object actions test suite It should build a
|
||||
"flatIndexFieldMetadatas": [],
|
||||
"id": Any<String>,
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": undefined,
|
||||
"indexWhereClause": null,
|
||||
"isCustom": false,
|
||||
"isUnique": false,
|
||||
"name": "defaultFlatIndexMetadataName",
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock';
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
{
|
||||
title: 'It should build an create_index action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build an delete_index and a create_index action ( the way we handle update )',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
});
|
||||
const fromFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
}),
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
{
|
||||
...flatIndexMetadata,
|
||||
name: 'new index name',
|
||||
isUnique: false,
|
||||
indexWhereClause: 'new index where clause',
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
deleteIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_index action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should not infer any actions as from and to indexes are identical',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
-5
@@ -7,7 +7,6 @@ import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modul
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
|
||||
import { WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case';
|
||||
import { WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case';
|
||||
import { WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case';
|
||||
import {
|
||||
type CamelCasedWorkspaceMigrationActionsType,
|
||||
@@ -31,10 +30,6 @@ const allWorkspaceBuilderTestCases: {
|
||||
label: 'field',
|
||||
testCases: WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES,
|
||||
},
|
||||
{
|
||||
label: 'index',
|
||||
testCases: WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES,
|
||||
},
|
||||
];
|
||||
|
||||
// TODO prastoin add coverage to infer deletion from missing entities
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { compareTwoFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/compare-two-flat-index-metadata.util';
|
||||
import {
|
||||
FlatEntityUpdateValidationArgs,
|
||||
FlatEntityValidationArgs,
|
||||
FlatEntityValidationReturnType,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import {
|
||||
getWorkspaceMigrationV2CreateIndexAction,
|
||||
getWorkspaceMigrationV2DeleteIndexAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
|
||||
import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-index-metadata-validator.service';
|
||||
|
||||
export type IndexRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
FlatIndexMetadata,
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
IndexRelatedFlatEntityMaps
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatIndexValidatorService: FlatIndexValidatorService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatIndexToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatIndexMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatIndexValidatorService.validateFlatIndexCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatIndexToValidate,
|
||||
optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: getWorkspaceMigrationV2CreateIndexAction(flatIndexToValidate),
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatIndexToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatIndexMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatIndexValidatorService.validateFlatIndexDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatIndexToValidate,
|
||||
optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: getWorkspaceMigrationV2DeleteIndexAction(flatIndexToValidate),
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: { from: fromFlatIndex, to: toFlatIndex },
|
||||
optimisticFlatEntityMaps: optimisticFlatIndexMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
const indexUpdatedProperties = compareTwoFlatIndexMetadata({
|
||||
fromFlatIndexMetadata: fromFlatIndex,
|
||||
toFlatIndexMetadata: toFlatIndex,
|
||||
});
|
||||
|
||||
if (indexUpdatedProperties.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const deletionValidationResult =
|
||||
this.flatIndexValidatorService.validateFlatIndexDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatIndexToValidate: fromFlatIndex,
|
||||
optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (deletionValidationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...deletionValidationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const creationValidationResult =
|
||||
this.flatIndexValidatorService.validateFlatIndexCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatIndexToValidate: toFlatIndex,
|
||||
optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (creationValidationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...creationValidationResult,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: [
|
||||
getWorkspaceMigrationV2DeleteIndexAction(fromFlatIndex),
|
||||
getWorkspaceMigrationV2CreateIndexAction(toFlatIndex),
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
import { compareTwoFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/compare-two-flat-index-metadata.util';
|
||||
import { type WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-index-matrix.util';
|
||||
import {
|
||||
getWorkspaceMigrationV2CreateIndexAction,
|
||||
getWorkspaceMigrationV2DeleteIndexAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
|
||||
|
||||
type BuildWorkspaceMigrationIndexActionsArgs = {
|
||||
objectMetadataDeletedCreatedUpdatedIndex: UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[];
|
||||
inferDeletionFromMissingObjectFieldIndex: boolean;
|
||||
};
|
||||
export const buildWorkspaceMigrationIndexActions = ({
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
objectMetadataDeletedCreatedUpdatedIndex,
|
||||
}: BuildWorkspaceMigrationIndexActionsArgs): WorkspaceMigrationIndexActionV2[] => {
|
||||
let allUpdatedObjectMetadataIndexActions: WorkspaceMigrationIndexActionV2[] =
|
||||
[];
|
||||
|
||||
for (const {
|
||||
createdIndexMetadata,
|
||||
deletedIndexMetadata,
|
||||
updatedIndexMetadata,
|
||||
} of objectMetadataDeletedCreatedUpdatedIndex) {
|
||||
const updatedDeleteAndCreateIndexActions =
|
||||
updatedIndexMetadata.flatMap<WorkspaceMigrationIndexActionV2>(
|
||||
({ to, from }) => {
|
||||
const updates = compareTwoFlatIndexMetadata({ from, to });
|
||||
|
||||
if (updates.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
getWorkspaceMigrationV2DeleteIndexAction(from),
|
||||
getWorkspaceMigrationV2CreateIndexAction(to),
|
||||
];
|
||||
},
|
||||
);
|
||||
|
||||
const createIndexActions = createdIndexMetadata.map(
|
||||
getWorkspaceMigrationV2CreateIndexAction,
|
||||
);
|
||||
const deleteIndexActions = inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedIndexMetadata.map(getWorkspaceMigrationV2DeleteIndexAction)
|
||||
: [];
|
||||
|
||||
allUpdatedObjectMetadataIndexActions =
|
||||
allUpdatedObjectMetadataIndexActions.concat([
|
||||
...createIndexActions,
|
||||
...deleteIndexActions,
|
||||
...updatedDeleteAndCreateIndexActions,
|
||||
]);
|
||||
}
|
||||
|
||||
return allUpdatedObjectMetadataIndexActions;
|
||||
};
|
||||
+1
@@ -8,6 +8,7 @@ export type FlatEntityValidationError<TCode extends string = string> = {
|
||||
value?: unknown;
|
||||
};
|
||||
|
||||
// Should be improved to be scoped to a given set of actions
|
||||
export type FailedFlatEntityValidation<T extends FlatEntity> = {
|
||||
type: WorkspaceMigrationActionTypeV2;
|
||||
errors: FlatEntityValidationError[];
|
||||
|
||||
+18
-5
@@ -1,6 +1,7 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
@@ -86,14 +87,14 @@ export type FlatEntityValidationReturnType<
|
||||
> =
|
||||
| {
|
||||
status: 'success';
|
||||
action: TActions;
|
||||
action: TActions | TActions[];
|
||||
}
|
||||
| ({
|
||||
status: 'fail';
|
||||
} & FailedFlatEntityValidation<TFlatEntity>);
|
||||
|
||||
export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
> {
|
||||
@@ -147,7 +148,11 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.created.push(validationResult.action);
|
||||
validateAndBuildResult.created.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
? validationResult.action
|
||||
: [validationResult.action]),
|
||||
);
|
||||
}
|
||||
|
||||
for (const flatEntityToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
@@ -169,7 +174,11 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.deleted.push(validationResult.action);
|
||||
validateAndBuildResult.deleted.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
? validationResult.action
|
||||
: [validationResult.action]),
|
||||
);
|
||||
}
|
||||
|
||||
for (const flatEntityUpdate of updated) {
|
||||
@@ -193,7 +202,11 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.updated.push(validationResult.action);
|
||||
validateAndBuildResult.updated.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
? validationResult.action
|
||||
: [validationResult.action]),
|
||||
);
|
||||
}
|
||||
|
||||
if (validateAndBuildResult.failed.length > 0) {
|
||||
|
||||
-15
@@ -7,12 +7,10 @@ import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-ob
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util';
|
||||
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { buildWorkspaceMigrationIndexActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import { computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util';
|
||||
import { computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-index-matrix.util';
|
||||
import { getWorkspaceMigrationV2CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
|
||||
|
||||
export type WorkspaceMigrationV2BuilderOptions = {
|
||||
@@ -70,12 +68,6 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
to: toFlatObjectMetadatas,
|
||||
});
|
||||
|
||||
// Should be handled separately from objects
|
||||
const objectMetadataDeletedCreatedUpdatedIndex =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix(
|
||||
updatedFlatObjectMetadatas,
|
||||
);
|
||||
|
||||
// Should be handled separately from objects
|
||||
const objectMetadataDeletedCreatedUpdatedFields =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||
@@ -114,12 +106,6 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
),
|
||||
);
|
||||
|
||||
const indexWorkspaceMigrationActions = buildWorkspaceMigrationIndexActions({
|
||||
objectMetadataDeletedCreatedUpdatedIndex,
|
||||
inferDeletionFromMissingObjectFieldIndex:
|
||||
buildOptions.inferDeletionFromMissingEntities,
|
||||
});
|
||||
|
||||
const allValidateAndBuildResultFailures = [
|
||||
...objectActionsValidateAndBuildResult.failed,
|
||||
...fieldActionsValidateAndBuildResult.failed,
|
||||
@@ -146,7 +132,6 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
...fieldActionsValidateAndBuildResult.created, // Should be handled separately from objects
|
||||
...fieldActionsValidateAndBuildResult.updated, // Should be handled separately from objects
|
||||
...createdObjectMetadataCreateIndexActions,
|
||||
...indexWorkspaceMigrationActions, // Should be handled separately from objects
|
||||
],
|
||||
},
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import { type FieldMetadataType, type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-properties-to-compare.type';
|
||||
|
||||
export type FlatFieldMetadataPropertyUpdate<
|
||||
P extends FlatFieldMetadataPropertiesToCompare,
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
> = {
|
||||
property: P;
|
||||
} & FromTo<FieldMetadataEntity<T>[P]>;
|
||||
-1
@@ -10,7 +10,6 @@ export type WorkspaceMigrationActionV2 =
|
||||
| WorkspaceMigrationIndexActionV2
|
||||
| WorkspaceMigrationViewActionV2
|
||||
| WorkspaceMigrationViewFieldActionV2;
|
||||
|
||||
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];
|
||||
|
||||
export type ExtractAction<T extends WorkspaceMigrationActionTypeV2> = Extract<
|
||||
|
||||
+5
-2
@@ -1,6 +1,6 @@
|
||||
import { type FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-properties-to-compare.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatFieldMetadataPropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-field-metadata-property-update.type';
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
|
||||
export type CreateFieldAction = {
|
||||
type: 'create_field';
|
||||
@@ -14,7 +14,10 @@ export type UpdateFieldAction = {
|
||||
objectMetadataId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatFieldMetadataPropertiesToCompare]: FlatFieldMetadataPropertyUpdate<P>;
|
||||
[P in FlatFieldMetadataPropertiesToCompare]: PropertyUpdate<
|
||||
FlatFieldMetadata,
|
||||
P
|
||||
>;
|
||||
}[FlatFieldMetadataPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
+9
-7
@@ -1,8 +1,9 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
type FlatObjectMetadata,
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
|
||||
export type CreateObjectAction = {
|
||||
@@ -16,9 +17,10 @@ export type UpdateObjectAction = {
|
||||
objectMetadataId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatObjectMetadataPropertiesToCompare]: {
|
||||
property: P;
|
||||
} & FromTo<ObjectMetadataEntity[P]>;
|
||||
[P in FlatObjectMetadataPropertiesToCompare]: PropertyUpdate<
|
||||
FlatObjectMetadata,
|
||||
P
|
||||
>;
|
||||
}[FlatObjectMetadataPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatEntityMapsExceptionCode } from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { IndexExceptionCode } from 'src/engine/metadata-modules/flat-index-metadata/exceptions/index-exception-code';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { IndexRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
type IndexValidationArgs = {
|
||||
flatIndexToValidate: FlatIndexMetadata;
|
||||
optimisticFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
dependencyOptimisticFlatEntityMaps: IndexRelatedFlatEntityMaps;
|
||||
};
|
||||
@Injectable()
|
||||
export class FlatIndexValidatorService {
|
||||
public validateFlatIndexDeletion({
|
||||
optimisticFlatIndexMaps,
|
||||
flatIndexToValidate: { id: indexIdToDelete },
|
||||
}: IndexValidationArgs): FailedFlatEntityValidation<FlatIndexMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatIndexMetadata> = {
|
||||
type: 'delete_index',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: indexIdToDelete,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatIndexToDelete = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: indexIdToDelete,
|
||||
flatEntityMaps: optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatIndexToDelete)) {
|
||||
validationResult.errors.push({
|
||||
code: FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
message: t`Index to delete not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public validateFlatIndexCreation({
|
||||
flatIndexToValidate,
|
||||
optimisticFlatIndexMaps,
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
}: {
|
||||
flatIndexToValidate: FlatIndexMetadata;
|
||||
optimisticFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
dependencyOptimisticFlatEntityMaps: IndexRelatedFlatEntityMaps;
|
||||
}): FailedFlatEntityValidation<FlatIndexMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatIndexMetadata> = {
|
||||
type: 'create_index',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatIndexToValidate.id,
|
||||
name: flatIndexToValidate.name,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatIndex = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatIndexToValidate.id,
|
||||
flatEntityMaps: optimisticFlatIndexMaps,
|
||||
});
|
||||
|
||||
if (isDefined(existingFlatIndex)) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_ALREADY_EXISTS,
|
||||
message: t`Index with same id already exists`,
|
||||
userFriendlyMessage: t`Index already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
const relatedObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatIndexToValidate.objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_OBJECT_NOT_FOUND,
|
||||
message: t`Could not find index related object metadata`,
|
||||
userFriendlyMessage: t`Index related object not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const allExistingFlatIndex = Object.values(
|
||||
optimisticFlatIndexMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
const existingFlatIndexOnName = allExistingFlatIndex.some(
|
||||
(flatIndexMetadata) =>
|
||||
flatIndexMetadata.name.toLocaleUpperCase() ===
|
||||
flatIndexToValidate.name.toLocaleUpperCase(),
|
||||
);
|
||||
|
||||
if (isDefined(existingFlatIndexOnName)) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_EMPTY_FIELDS,
|
||||
message: t`Index with same name already exists`,
|
||||
userFriendlyMessage: t`Index with same name already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
if (flatIndexToValidate.flatIndexFieldMetadatas.length === 0) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_EMPTY_FIELDS,
|
||||
message: t`Index must have at least one field`,
|
||||
userFriendlyMessage: t`An index must contain at least one field`,
|
||||
});
|
||||
} else {
|
||||
flatIndexToValidate.flatIndexFieldMetadatas.forEach((flatIndexField) => {
|
||||
const relatedFlatField =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: flatIndexField.fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatField)) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_FIELD_NOT_FOUND,
|
||||
message: t`Could not find index field related field metadata`,
|
||||
userFriendlyMessage: t`Field referenced in index does not exist`,
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
relatedFlatField.objectMetadataId !==
|
||||
flatIndexToValidate.objectMetadataId
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_FIELD_WRONG_OBJECT,
|
||||
message: t`Field does not belong to the indexed object`,
|
||||
userFriendlyMessage: t`Field cannot be indexed as it belongs to a different object`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (flatIndexField.indexMetadataId !== flatIndexToValidate.id) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_FIELD_INVALID_REFERENCE,
|
||||
message: t`Index field references incorrect index metadata`,
|
||||
userFriendlyMessage: t`Index field has an invalid reference`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
allExistingFlatIndex.some(({ flatIndexFieldMetadatas }) =>
|
||||
flatIndexFieldMetadatas.some(({ id }) => id === flatIndexField.id),
|
||||
)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: IndexExceptionCode.INDEX_FIELD_ID_DUPLICATE,
|
||||
message: t`Index field ID already exists in another index`,
|
||||
userFriendlyMessage: t`Field ID is already used in another index`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -1,10 +1,19 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-index-metadata-validator.service';
|
||||
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
|
||||
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
|
||||
|
||||
@Module({
|
||||
providers: [FlatViewValidatorService, FlatViewFieldValidatorService],
|
||||
exports: [FlatViewValidatorService, FlatViewFieldValidatorService],
|
||||
providers: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
],
|
||||
exports: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMigrationBuilderValidatorsModule {}
|
||||
|
||||
+3
@@ -5,6 +5,7 @@ import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modul
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
@@ -24,12 +25,14 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
FlatViewValidatorService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
FlatViewFieldValidatorService,
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
|
||||
const { flatFieldMetadata } = action;
|
||||
|
||||
await fieldMetadataRepository.save(flatFieldMetadata);
|
||||
await fieldMetadataRepository.insert(flatFieldMetadata);
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
|
||||
+5
-5
@@ -29,7 +29,7 @@ import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { FlatFieldMetadataPropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-field-metadata-property-update.type';
|
||||
import { PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
import { type UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { serializeDefaultValueV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/serialize-default-value-v2.util';
|
||||
import {
|
||||
@@ -54,7 +54,7 @@ type UpdateFieldPropertyUpdateHandlerArgs<
|
||||
schemaName: string;
|
||||
tableName: string;
|
||||
flatFieldMetadata: FlatFieldMetadata<T>;
|
||||
update: FlatFieldMetadataPropertyUpdate<P, T>;
|
||||
update: PropertyUpdate<FlatFieldMetadata<T>, P>;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -199,9 +199,9 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
update: update as FlatFieldMetadataPropertyUpdate<
|
||||
'settings',
|
||||
MorphOrRelationFieldMetadataType
|
||||
update: update as PropertyUpdate<
|
||||
FlatFieldMetadata<MorphOrRelationFieldMetadataType>,
|
||||
'settings'
|
||||
>,
|
||||
});
|
||||
}
|
||||
|
||||
+70
-8
@@ -6,28 +6,90 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/get-workspace-schema-context-for-migration.util';
|
||||
|
||||
@Injectable()
|
||||
export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'create_index',
|
||||
) {
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps(
|
||||
_args: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateIndexAction>,
|
||||
): Partial<AllFlatEntityMaps> {
|
||||
return {};
|
||||
constructor(
|
||||
private readonly workspaceSchemaManagerService: WorkspaceSchemaManagerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps: { flatIndexMaps },
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateIndexAction>): Partial<AllFlatEntityMaps> {
|
||||
return {
|
||||
flatIndexMaps: addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: action.flatIndexMetadata,
|
||||
flatEntityMaps: flatIndexMaps,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
const { action, queryRunner } = context;
|
||||
const indexMetadataRepository =
|
||||
queryRunner.manager.getRepository<IndexMetadataEntity>(
|
||||
IndexMetadataEntity,
|
||||
);
|
||||
|
||||
const {
|
||||
flatIndexMetadata: { flatIndexFieldMetadatas, ...rest },
|
||||
} = action;
|
||||
|
||||
await indexMetadataRepository.insert({
|
||||
indexFieldMetadatas: flatIndexFieldMetadatas,
|
||||
...rest,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
const {
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
action: { flatIndexMetadata },
|
||||
queryRunner,
|
||||
workspaceId,
|
||||
} = context;
|
||||
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatIndexMetadata.objectMetadataId,
|
||||
});
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
const quotedColumns = flatIndexMetadata.flatIndexFieldMetadatas.map(
|
||||
(column) => `"${column}"`,
|
||||
);
|
||||
|
||||
await this.workspaceSchemaManagerService.indexManager.createIndex({
|
||||
index: {
|
||||
columns: quotedColumns,
|
||||
name: flatIndexMetadata.name,
|
||||
isUnique: flatIndexMetadata.isUnique,
|
||||
type: flatIndexMetadata.indexType,
|
||||
where: flatIndexMetadata.indexWhereClause ?? undefined,
|
||||
},
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+56
-8
@@ -6,28 +6,76 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { type DeleteIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/get-workspace-schema-context-for-migration.util';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteIndexActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'delete_index',
|
||||
) {
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps(
|
||||
_args: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteIndexAction>,
|
||||
): Partial<AllFlatEntityMaps> {
|
||||
return {};
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps: { flatIndexMaps },
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteIndexAction>): Partial<AllFlatEntityMaps> {
|
||||
return {
|
||||
flatIndexMaps: deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: action.flatIndexMetadataId,
|
||||
flatEntityMaps: flatIndexMaps,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteIndexAction>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteIndexAction>,
|
||||
): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
const { action, queryRunner } = context;
|
||||
const indexMetadataRepository =
|
||||
queryRunner.manager.getRepository<IndexMetadataEntity>(
|
||||
IndexMetadataEntity,
|
||||
);
|
||||
|
||||
const { flatIndexMetadataId } = action;
|
||||
|
||||
await indexMetadataRepository.delete({
|
||||
id: flatIndexMetadataId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteIndexAction>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteIndexAction>,
|
||||
): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
const {
|
||||
action,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatIndexMaps },
|
||||
queryRunner,
|
||||
workspaceId,
|
||||
} = context;
|
||||
|
||||
const flatIndexMetadataToDelete = findFlatEntityByIdInFlatEntityMapsOrThrow(
|
||||
{
|
||||
flatEntityId: action.flatIndexMetadataId,
|
||||
flatEntityMaps: flatIndexMaps,
|
||||
},
|
||||
);
|
||||
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatIndexMetadataToDelete.objectMetadataId,
|
||||
});
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
await queryRunner.dropIndex(
|
||||
`${schemaName}.${tableName}`,
|
||||
flatIndexMetadataToDelete.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
await objectMetadataRepository.save({
|
||||
await objectMetadataRepository.insert({
|
||||
...flatObjectMetadataWithoutFields,
|
||||
dataSourceId: lastDataSourceMetadata.id,
|
||||
targetTableName: 'DEPRECATED',
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne
|
||||
const viewFieldRepository =
|
||||
queryRunner.manager.getRepository<ViewFieldEntity>(ViewFieldEntity);
|
||||
|
||||
await viewFieldRepository.save({
|
||||
await viewFieldRepository.insert({
|
||||
...viewField,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
const viewRepository =
|
||||
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
|
||||
|
||||
await viewRepository.save({
|
||||
await viewRepository.insert({
|
||||
...view,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
+6
-1
@@ -2,7 +2,12 @@ import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-meta
|
||||
|
||||
export type PartialIndexMetadata = Omit<
|
||||
IndexMetadataEntity,
|
||||
'id' | 'objectMetadata' | 'indexFieldMetadatas' | 'createdAt' | 'updatedAt'
|
||||
| 'id'
|
||||
| 'objectMetadata'
|
||||
| 'indexFieldMetadatas'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'universalIdentifier'
|
||||
> & {
|
||||
columns: string[];
|
||||
};
|
||||
|
||||
+10
-6
@@ -5,6 +5,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
@@ -28,9 +29,10 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -45,6 +47,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
@@ -68,9 +71,10 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
|
||||
+350
-210
File diff suppressed because it is too large
Load Diff
+340
-204
File diff suppressed because it is too large
Load Diff
+85
-51
@@ -5,6 +5,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -44,9 +45,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -61,6 +63,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -84,9 +87,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -101,6 +105,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -119,9 +124,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -136,6 +142,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -154,9 +161,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -171,6 +179,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -200,9 +209,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -217,6 +227,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -240,9 +251,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -257,6 +269,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -280,9 +293,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -297,6 +311,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -326,9 +341,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -343,6 +359,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -369,9 +386,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -386,6 +404,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -412,9 +431,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -429,6 +449,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -454,9 +475,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -471,6 +493,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -489,9 +512,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -506,6 +530,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -524,9 +549,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -541,6 +567,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -559,9 +586,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -576,6 +604,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -597,9 +626,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -614,6 +644,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -633,9 +664,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -650,6 +682,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -668,9 +701,10 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
|
||||
+105
-63
@@ -5,6 +5,7 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -27,9 +28,10 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -44,6 +46,7 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -66,9 +69,10 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -83,6 +87,7 @@ exports[`Object metadata creation should fail v2 when labelPlural is empty 1`] =
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -105,9 +110,10 @@ exports[`Object metadata creation should fail v2 when labelPlural is empty 1`] =
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -122,6 +128,7 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -144,9 +151,10 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -161,6 +169,7 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -183,9 +192,10 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -200,6 +210,7 @@ exports[`Object metadata creation should fail v2 when labelSingular is empty 1`]
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -222,9 +233,10 @@ exports[`Object metadata creation should fail v2 when labelSingular is empty 1`]
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -239,6 +251,7 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] =
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -262,9 +275,10 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] =
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -279,6 +293,7 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -302,9 +317,10 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -319,6 +335,7 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -421,9 +438,10 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -438,6 +456,7 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -460,9 +479,10 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -477,6 +497,7 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -499,9 +520,10 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -516,6 +538,7 @@ exports[`Object metadata creation should fail v2 when namePlural is an empty str
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -543,9 +566,10 @@ exports[`Object metadata creation should fail v2 when namePlural is an empty str
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -560,6 +584,7 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -587,9 +612,10 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -604,6 +630,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -741,9 +768,10 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -758,6 +786,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -895,9 +924,10 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -912,6 +942,7 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1014,9 +1045,10 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -1031,6 +1063,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1133,9 +1166,10 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -1150,6 +1184,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is an empty s
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1287,9 +1322,10 @@ exports[`Object metadata creation should fail v2 when nameSingular is an empty s
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -1304,6 +1340,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1441,9 +1478,10 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -1458,6 +1496,7 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] =
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1481,9 +1520,10 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] =
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
@@ -1498,6 +1538,7 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"index": [],
|
||||
"objectMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
@@ -1521,9 +1562,10 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidObjectMetadatas": 0,
|
||||
"invalidViewFields": 0,
|
||||
"invalidViews": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
|
||||
Reference in New Issue
Block a user