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:
Paul Rastoin
2025-09-17 15:02:43 +02:00
committed by GitHub
parent 6e47c1b5c6
commit 2d8669ae71
55 changed files with 1692 additions and 1064 deletions
@@ -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;
};
@@ -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: [],
@@ -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)`,
});
};
@@ -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 =
@@ -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;
};
@@ -0,0 +1,5 @@
import { type FromTo } from 'twenty-shared/types';
export type PropertyUpdate<T, P extends keyof T> = {
property: P;
} & FromTo<T[P]>;
@@ -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",
@@ -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,
},
},
},
];
@@ -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
@@ -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),
],
};
}
}
@@ -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;
};
@@ -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[];
@@ -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) {
@@ -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:
@@ -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]>;
@@ -10,7 +10,6 @@ export type WorkspaceMigrationActionV2 =
| WorkspaceMigrationIndexActionV2
| WorkspaceMigrationViewActionV2
| WorkspaceMigrationViewFieldActionV2;
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];
export type ExtractAction<T extends WorkspaceMigrationActionTypeV2> = Extract<
@@ -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]
>;
};
@@ -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]
>;
};
@@ -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;
}
}
@@ -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 {}
@@ -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,
@@ -57,7 +57,7 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
const { flatFieldMetadata } = action;
await fieldMetadataRepository.save(flatFieldMetadata);
await fieldMetadataRepository.insert(flatFieldMetadata);
}
async executeForWorkspaceSchema(
@@ -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'
>,
});
}
@@ -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,
});
}
}
@@ -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,
);
}
}
@@ -80,7 +80,7 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
order: { createdAt: 'DESC' },
});
await objectMetadataRepository.save({
await objectMetadataRepository.insert({
...flatObjectMetadataWithoutFields,
dataSourceId: lastDataSourceMetadata.id,
targetTableName: 'DEPRECATED',
@@ -45,7 +45,7 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne
const viewFieldRepository =
queryRunner.manager.getRepository<ViewFieldEntity>(ViewFieldEntity);
await viewFieldRepository.save({
await viewFieldRepository.insert({
...viewField,
workspaceId,
});
@@ -45,7 +45,7 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
const viewRepository =
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
await viewRepository.save({
await viewRepository.insert({
...view,
workspaceId,
});
@@ -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[];
};