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:
+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[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user