Add workspace migration orchestrator + refactor builders + view/viewField action builders (#14383)
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class FlatEntityException extends CustomException {
|
||||
code: FlatEntityExceptionCode;
|
||||
|
||||
constructor(message: string, code: FlatEntityExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum FlatEntityExceptionCode {
|
||||
ENTITY_ALREADY_EXISTS = 'ENTITY_ALREADY_EXISTS',
|
||||
ENTITY_NOT_FOUND = 'ENTITY_NOT_FOUND',
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { type FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
|
||||
export type FlatEntityMaps<T extends FlatEntity> = {
|
||||
byId: Partial<Record<string, T>>;
|
||||
idByUniversalIdentifier: Partial<Record<string, string>>;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface FlatEntity {
|
||||
id: string;
|
||||
universalIdentifier: string;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { isDefined } from 'class-validator';
|
||||
|
||||
import {
|
||||
FlatEntityException,
|
||||
FlatEntityExceptionCode,
|
||||
} from 'src/engine/core-modules/common/exceptions/flat-entity.exception';
|
||||
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';
|
||||
|
||||
type AddFlatEntityToFlatEntityMapsOrThrowArgs<T extends FlatEntity> = {
|
||||
flatEntity: T;
|
||||
flatEntityMaps: FlatEntityMaps<T>;
|
||||
};
|
||||
|
||||
export const addFlatEntityToFlatEntityMapsOrThrow = <T extends FlatEntity>({
|
||||
flatEntity,
|
||||
flatEntityMaps,
|
||||
}: AddFlatEntityToFlatEntityMapsOrThrowArgs<T>): FlatEntityMaps<T> => {
|
||||
if (isDefined(flatEntityMaps.byId[flatEntity.id])) {
|
||||
throw new FlatEntityException(
|
||||
'addFlatEntityToFlatEntityMapsOrThrow: flat entity to add already exists',
|
||||
FlatEntityExceptionCode.ENTITY_ALREADY_EXISTS,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
byId: {
|
||||
...flatEntityMaps.byId,
|
||||
[flatEntity.id]: flatEntity,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
...flatEntityMaps.idByUniversalIdentifier,
|
||||
[flatEntity.universalIdentifier]: flatEntity.id,
|
||||
},
|
||||
};
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
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';
|
||||
import {
|
||||
FlatEntityException,
|
||||
FlatEntityExceptionCode,
|
||||
} from 'src/engine/core-modules/common/exceptions/flat-entity.exception';
|
||||
|
||||
export type DeleteFlatEntityFromFlatEntityMapsOrThrowArgs<
|
||||
T extends FlatEntity,
|
||||
> = {
|
||||
entityToDeleteId: string;
|
||||
flatEntityMaps: FlatEntityMaps<T>;
|
||||
};
|
||||
|
||||
export const deleteFlatEntityFromFlatEntityMapsOrThrow = <
|
||||
T extends FlatEntity,
|
||||
>({
|
||||
flatEntityMaps,
|
||||
entityToDeleteId,
|
||||
}: DeleteFlatEntityFromFlatEntityMapsOrThrowArgs<T>): FlatEntityMaps<T> => {
|
||||
if (!isDefined(flatEntityMaps.byId[entityToDeleteId])) {
|
||||
throw new FlatEntityException(
|
||||
'deleteFlatEntityFromFlatEntityMapsOrThrow: entity to delete not found',
|
||||
FlatEntityExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedIdByUniversalIdentifierEntries = Object.entries(
|
||||
flatEntityMaps.idByUniversalIdentifier,
|
||||
).filter(([_universalIdentifier, id]) => id !== entityToDeleteId);
|
||||
|
||||
return {
|
||||
byId: removePropertiesFromRecord(flatEntityMaps.byId, [entityToDeleteId]),
|
||||
idByUniversalIdentifier: Object.fromEntries(
|
||||
updatedIdByUniversalIdentifierEntries,
|
||||
),
|
||||
};
|
||||
};
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
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';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
|
||||
export type ReplaceFlatEntityInFlatEntityMapsOrThrowArgs<T extends FlatEntity> =
|
||||
{
|
||||
flatEntity: T;
|
||||
flatEntityMaps: FlatEntityMaps<T>;
|
||||
};
|
||||
|
||||
export const replaceFlatEntityInFlatEntityMapsOrThrow = <T extends FlatEntity>({
|
||||
flatEntity,
|
||||
flatEntityMaps,
|
||||
}: ReplaceFlatEntityInFlatEntityMapsOrThrowArgs<T>): FlatEntityMaps<T> => {
|
||||
const flatEntityMapsToReplace = deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps,
|
||||
entityToDeleteId: flatEntity.id,
|
||||
});
|
||||
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity,
|
||||
flatEntityMaps: flatEntityMapsToReplace,
|
||||
});
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ViewCacheService } from 'src/engine/core-modules/view/cache/services/view-cache.service';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([ViewEntity])],
|
||||
providers: [ViewCacheService],
|
||||
exports: [ViewCacheService],
|
||||
})
|
||||
export class ViewCacheModule {}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { generateFlatViewMaps } from 'src/engine/core-modules/view/flat-view/utils/generate-flat-view-maps.util';
|
||||
|
||||
type GetExistingOrRecomputeFlatViewMapsResult = {
|
||||
flatViewMaps: FlatViewMaps;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ViewCacheService {
|
||||
logger = new Logger(ViewCacheService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
) {}
|
||||
|
||||
async getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<GetExistingOrRecomputeFlatViewMapsResult> {
|
||||
// TODO: get from cache later
|
||||
const existingViews = await this.viewRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['viewFields'],
|
||||
select: {
|
||||
viewFields: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const existingFlatViewMaps = generateFlatViewMaps(existingViews);
|
||||
|
||||
return {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,10 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
|
||||
where: '"deletedAt" IS NULL',
|
||||
},
|
||||
)
|
||||
export class ViewFieldEntity extends SyncableEntity {
|
||||
export class ViewFieldEntity
|
||||
extends SyncableEntity
|
||||
implements Required<ViewFieldEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@@ -59,7 +62,7 @@ export class ViewFieldEntity extends SyncableEntity {
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
aggregateOperation?: AggregateOperations | null;
|
||||
aggregateOperation: AggregateOperations | null;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
viewId: string;
|
||||
@@ -74,7 +77,7 @@ export class ViewFieldEntity extends SyncableEntity {
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({ type: 'timestamptz' })
|
||||
deletedAt?: Date | null;
|
||||
deletedAt: Date | null;
|
||||
|
||||
@ManyToOne(() => Workspace, {
|
||||
onDelete: 'CASCADE',
|
||||
|
||||
@@ -31,7 +31,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
'workspaceId',
|
||||
'objectMetadataId',
|
||||
])
|
||||
export class ViewEntity extends SyncableEntity {
|
||||
export class ViewEntity extends SyncableEntity implements Required<ViewEntity> {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@@ -89,10 +89,10 @@ export class ViewEntity extends SyncableEntity {
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
kanbanAggregateOperation?: AggregateOperations | null;
|
||||
kanbanAggregateOperation: AggregateOperations | null;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
kanbanAggregateOperationFieldMetadataId?: string | null;
|
||||
kanbanAggregateOperationFieldMetadataId: string | null;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workspaceId: string;
|
||||
@@ -104,10 +104,10 @@ export class ViewEntity extends SyncableEntity {
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({ type: 'timestamptz' })
|
||||
deletedAt?: Date | null;
|
||||
deletedAt: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: 'text', default: null })
|
||||
anyFieldFilterValue?: string | null;
|
||||
anyFieldFilterValue: string | null;
|
||||
|
||||
@ManyToOne(() => Workspace, {
|
||||
onDelete: 'CASCADE',
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
appendCommonExceptionCode,
|
||||
CustomException,
|
||||
} from 'src/utils/custom-exception';
|
||||
|
||||
export class FlatViewException extends CustomException<
|
||||
keyof typeof FlatViewExceptionCode
|
||||
> {}
|
||||
|
||||
export const FlatViewExceptionCode = appendCommonExceptionCode({
|
||||
VIEW_NOT_FOUND: 'VIEW_NOT_FOUND',
|
||||
VIEW_ALREADY_EXISTS: 'VIEW_ALREADY_EXISTS',
|
||||
} as const);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE = [
|
||||
'isVisible',
|
||||
'size',
|
||||
'position',
|
||||
'aggregateOperation',
|
||||
] as const satisfies (keyof FlatViewField)[];
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const FLAT_VIEW_PROPERTIES_TO_COMPARE = [
|
||||
'name',
|
||||
'type',
|
||||
'key',
|
||||
'isCompact',
|
||||
'openRecordIn',
|
||||
'kanbanAggregateOperation',
|
||||
'kanbanAggregateOperationFieldMetadataId',
|
||||
'position',
|
||||
'anyFieldFilterValue',
|
||||
'icon',
|
||||
] as const satisfies (keyof FlatView)[];
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { type ViewEntityRelationProperties } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const VIEW_ENTITY_RELATION_PROPERTIES = [
|
||||
'objectMetadata',
|
||||
'viewFields',
|
||||
'viewFilters',
|
||||
'viewFilterGroups',
|
||||
'viewGroups',
|
||||
'viewSorts',
|
||||
'workspace',
|
||||
] as const satisfies ViewEntityRelationProperties[];
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { type ViewFieldEntityRelationProperties } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const VIEW_FIELD_ENTITY_RELATION_PROPERTIES = [
|
||||
'fieldMetadata',
|
||||
'view',
|
||||
'workspace',
|
||||
] as const satisfies ViewFieldEntityRelationProperties[];
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export type FailedFlatViewFieldValidationMinimalInformation = Partial<
|
||||
Pick<FlatViewField, 'id' | 'fieldMetadataId' | 'viewId'>
|
||||
>;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type FailedFlatViewFieldValidationMinimalInformation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-field-validation-minimal-information.type';
|
||||
import { type FlatViewFieldValidationError } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-validation-error.type';
|
||||
import { type WorkspaceMigrationViewFieldActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
|
||||
export type FailedFlatViewFieldValidation = {
|
||||
type: WorkspaceMigrationViewFieldActionTypeV2;
|
||||
viewFieldLevelErrors: FlatViewFieldValidationError[];
|
||||
failedViewFieldValidationMinimalInformation: Partial<FailedFlatViewFieldValidationMinimalInformation>;
|
||||
};
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export type FailedFlatViewValidationMinimalInformation = Partial<
|
||||
Pick<FlatView, 'id' | 'name' | 'objectMetadataId'>
|
||||
>;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type FailedFlatViewValidationMinimalInformation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation-minimal-information.type';
|
||||
import { type FlatViewValidationError } from 'src/engine/core-modules/view/flat-view/types/flat-view-validation-error.type';
|
||||
import { type WorkspaceMigrationViewActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
|
||||
export type FailedFlatViewValidation = {
|
||||
type: WorkspaceMigrationViewActionTypeV2;
|
||||
viewLevelErrors: FlatViewValidationError[];
|
||||
failedViewValidationMinimalInformation: Partial<FailedFlatViewValidationMinimalInformation>;
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export type FlatViewFieldMaps = FlatEntityMaps<FlatViewField>;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE } from 'src/engine/core-modules/view/flat-view/constants/flat-view-field-properties-to-compare.constant';
|
||||
|
||||
export type FlatViewFieldPropertiesToCompare =
|
||||
(typeof FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE)[number];
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export type FlatViewFieldValidationError = {
|
||||
message: string;
|
||||
code: string;
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
|
||||
export type ViewFieldEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
ViewFieldEntity,
|
||||
FieldMetadataEntity | ViewEntity | Workspace
|
||||
>;
|
||||
|
||||
export type FlatViewField = Omit<
|
||||
ViewFieldEntity,
|
||||
ViewFieldEntityRelationProperties
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export type FlatViewMaps = FlatEntityMaps<FlatView>;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FLAT_VIEW_PROPERTIES_TO_COMPARE } from 'src/engine/core-modules/view/flat-view/constants/flat-view-properies-to-compare.constant';
|
||||
|
||||
export type FlatViewPropertiesToCompare =
|
||||
(typeof FLAT_VIEW_PROPERTIES_TO_COMPARE)[number];
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { type ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
|
||||
export type FlatViewValidationError = {
|
||||
code: ViewExceptionCode;
|
||||
message: string;
|
||||
userFriendlyMessage?: string;
|
||||
value?: unknown;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { type ViewFilterGroupEntity } from 'src/engine/core-modules/view/entities/view-filter-group.entity';
|
||||
import { type ViewFilterEntity } from 'src/engine/core-modules/view/entities/view-filter.entity';
|
||||
import { type ViewGroupEntity } from 'src/engine/core-modules/view/entities/view-group.entity';
|
||||
import { type ViewSortEntity } from 'src/engine/core-modules/view/entities/view-sort.entity';
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
|
||||
export type ViewEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
ViewEntity,
|
||||
| ObjectMetadataEntity
|
||||
| ViewFieldEntity
|
||||
| ViewSortEntity
|
||||
| ViewFilterEntity
|
||||
| ViewGroupEntity
|
||||
| ViewFilterGroupEntity
|
||||
| Workspace
|
||||
>;
|
||||
|
||||
export type FlatView = Omit<ViewEntity, ViewEntityRelationProperties> & {
|
||||
universalIdentifier: string;
|
||||
viewFieldIds: string[];
|
||||
};
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import diff from 'microdiff';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE } from 'src/engine/core-modules/view/flat-view/constants/flat-view-field-properties-to-compare.constant';
|
||||
import { type FlatViewFieldPropertiesToCompare } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-properties-to-compare.type';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type UpdateViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
type GetWorkspaceMigrationUpdateViewFieldActionArgs = FromTo<
|
||||
FlatViewField,
|
||||
'FlatViewField'
|
||||
>;
|
||||
|
||||
export const compareTwoFlatViewField = ({
|
||||
fromFlatViewField,
|
||||
toFlatViewField,
|
||||
}: GetWorkspaceMigrationUpdateViewFieldActionArgs) => {
|
||||
const transformMetadataForComparisonParameters = {
|
||||
shouldIgnoreProperty: (property: string) =>
|
||||
!FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE.includes(
|
||||
property as FlatViewFieldPropertiesToCompare,
|
||||
),
|
||||
propertiesToStringify: [],
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
fromFlatViewField,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
toFlatViewField,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
|
||||
const flatViewFieldDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
return flatViewFieldDifferences.flatMap<
|
||||
UpdateViewFieldAction['updates'][number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatViewFieldPropertiesToCompare;
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occurs, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import diff from 'microdiff';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FLAT_VIEW_PROPERTIES_TO_COMPARE } from 'src/engine/core-modules/view/flat-view/constants/flat-view-properies-to-compare.constant';
|
||||
import { type FlatViewPropertiesToCompare } from 'src/engine/core-modules/view/flat-view/types/flat-view-properties-to-compare.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { type UpdateViewAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
type GetWorkspaceMigrationUpdateViewActionArgs = FromTo<FlatView, 'FlatView'>;
|
||||
|
||||
export const compareTwoFlatView = ({
|
||||
fromFlatView,
|
||||
toFlatView,
|
||||
}: GetWorkspaceMigrationUpdateViewActionArgs) => {
|
||||
const transformMetadataForComparisonParameters = {
|
||||
shouldIgnoreProperty: (property: string) =>
|
||||
!FLAT_VIEW_PROPERTIES_TO_COMPARE.includes(
|
||||
property as FlatViewPropertiesToCompare,
|
||||
),
|
||||
propertiesToStringify: [],
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
fromFlatView,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
toFlatView,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
|
||||
const flatViewDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
return flatViewDifferences.flatMap<UpdateViewAction['updates'][number]>(
|
||||
(difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatViewPropertiesToCompare;
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occurs, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const fromPartialFlatViewFieldToFlatViewFieldWithDefault = (
|
||||
partialFlatViewField: Partial<FlatViewField>,
|
||||
): FlatViewField => {
|
||||
const createdAt = new Date();
|
||||
const viewFieldId = partialFlatViewField.id ?? v4();
|
||||
|
||||
return {
|
||||
...partialFlatViewField,
|
||||
id: viewFieldId,
|
||||
fieldMetadataId: partialFlatViewField.fieldMetadataId ?? '',
|
||||
isVisible: partialFlatViewField.isVisible ?? true,
|
||||
size: partialFlatViewField.size ?? 0,
|
||||
position: partialFlatViewField.position ?? 0,
|
||||
aggregateOperation: partialFlatViewField.aggregateOperation ?? null,
|
||||
viewId: partialFlatViewField.viewId ?? '',
|
||||
workspaceId: partialFlatViewField.workspaceId ?? '',
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
universalIdentifier:
|
||||
partialFlatViewField.universalIdentifier ?? viewFieldId,
|
||||
};
|
||||
};
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ViewOpenRecordIn } from 'src/engine/core-modules/view/enums/view-open-record-in';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const fromPartialFlatViewToFlatViewWithDefault = (
|
||||
partialFlatView: Partial<FlatView>,
|
||||
): FlatView => {
|
||||
const createdAt = new Date();
|
||||
const viewId = partialFlatView.id ?? v4();
|
||||
|
||||
return {
|
||||
...partialFlatView,
|
||||
id: viewId,
|
||||
name: partialFlatView.name ?? '',
|
||||
objectMetadataId: partialFlatView.objectMetadataId ?? '',
|
||||
type: partialFlatView.type ?? ViewType.TABLE,
|
||||
key: partialFlatView.key ?? null,
|
||||
icon: partialFlatView.icon ?? '',
|
||||
position: partialFlatView.position ?? 0,
|
||||
isCompact: partialFlatView.isCompact ?? false,
|
||||
isCustom: partialFlatView.isCustom ?? false,
|
||||
openRecordIn: partialFlatView.openRecordIn ?? ViewOpenRecordIn.SIDE_PANEL,
|
||||
kanbanAggregateOperation: partialFlatView.kanbanAggregateOperation ?? null,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
partialFlatView.kanbanAggregateOperationFieldMetadataId ?? null,
|
||||
workspaceId: partialFlatView.workspaceId ?? '',
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
anyFieldFilterValue: partialFlatView.anyFieldFilterValue ?? null,
|
||||
universalIdentifier: partialFlatView.universalIdentifier ?? viewId,
|
||||
viewFieldIds: partialFlatView.viewFieldIds ?? [],
|
||||
};
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { VIEW_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/view-entity-relation-properties.constant';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const fromViewEntityToFlatView = (viewEntity: ViewEntity): FlatView => {
|
||||
const viewEntityWithoutRelations = removePropertiesFromRecord(
|
||||
viewEntity,
|
||||
VIEW_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
return {
|
||||
...viewEntityWithoutRelations,
|
||||
universalIdentifier:
|
||||
viewEntityWithoutRelations.universalIdentifier ??
|
||||
viewEntityWithoutRelations.id,
|
||||
viewFieldIds: viewEntity.viewFields?.map((viewField) => viewField.id) ?? [],
|
||||
};
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { VIEW_FIELD_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/view-field-entity-relation-properties.constant';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const fromViewFieldEntityToFlatViewField = (
|
||||
viewFieldEntity: ViewFieldEntity,
|
||||
): FlatViewField => {
|
||||
const viewFieldEntityWithoutRelations = removePropertiesFromRecord(
|
||||
viewFieldEntity,
|
||||
VIEW_FIELD_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
return {
|
||||
...viewFieldEntityWithoutRelations,
|
||||
universalIdentifier:
|
||||
viewFieldEntityWithoutRelations.universalIdentifier ??
|
||||
viewFieldEntityWithoutRelations.id,
|
||||
};
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const generateFlatViewMaps = (
|
||||
viewCollection: ViewEntity[],
|
||||
): FlatViewMaps => {
|
||||
const flatViewMaps: FlatViewMaps = {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
for (const view of viewCollection) {
|
||||
const processedFlatView: FlatView = {
|
||||
...view,
|
||||
viewFieldIds: view.viewFields?.map((viewField) => viewField.id) ?? [],
|
||||
universalIdentifier: view.universalIdentifier ?? view.id, // TODO: should not fallback to id, to remove once we have universalIdentifier required
|
||||
};
|
||||
|
||||
flatViewMaps.byId[view.id] = processedFlatView;
|
||||
flatViewMaps.idByUniversalIdentifier[
|
||||
processedFlatView.universalIdentifier
|
||||
] = view.id;
|
||||
}
|
||||
|
||||
return flatViewMaps;
|
||||
};
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import {
|
||||
ViewFieldException,
|
||||
ViewFieldExceptionCode,
|
||||
ViewFieldExceptionMessageKey,
|
||||
generateViewFieldExceptionMessage,
|
||||
generateViewFieldUserFriendlyExceptionMessage,
|
||||
} from 'src/engine/core-modules/view/exceptions/view-field.exception';
|
||||
import { VIEW_FIELD_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/view-field-entity-relation-properties.constant';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { fromPartialFlatViewFieldToFlatViewFieldWithDefault } from 'src/engine/core-modules/view/flat-view/utils/from-partial-flat-view-field-to-flat-view-field-with-default.util';
|
||||
import { fromViewFieldEntityToFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/from-view-field-entity-to-flat-view-field.util';
|
||||
import { WorkspaceMigrationOrchestratorException } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-orchestrator-exception';
|
||||
import { WorkspaceMigrationBuildOrchestratorService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service';
|
||||
|
||||
@Injectable()
|
||||
export class ViewFieldV2Service {
|
||||
constructor(
|
||||
@InjectRepository(ViewFieldEntity)
|
||||
private readonly viewFieldRepository: Repository<ViewFieldEntity>,
|
||||
private readonly workspaceMigrationOrchestratorService: WorkspaceMigrationBuildOrchestratorService,
|
||||
) {}
|
||||
|
||||
// TODO: move to cache service
|
||||
private async getExistingFlatViewFieldMaps(
|
||||
workspaceId: string,
|
||||
): Promise<FlatViewFieldMaps> {
|
||||
const existingViewFields = await this.viewFieldRepository.find({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
const flatViewFieldMaps: FlatViewFieldMaps = {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
for (const viewFieldEntity of existingViewFields) {
|
||||
const flatViewField = fromViewFieldEntityToFlatViewField(viewFieldEntity);
|
||||
|
||||
flatViewFieldMaps.byId[flatViewField.id] = flatViewField;
|
||||
flatViewFieldMaps.idByUniversalIdentifier[
|
||||
flatViewField.universalIdentifier
|
||||
] = flatViewField.id;
|
||||
}
|
||||
|
||||
return flatViewFieldMaps;
|
||||
}
|
||||
|
||||
async createOne(
|
||||
viewFieldData: Partial<ViewFieldEntity>,
|
||||
): Promise<ViewFieldEntity> {
|
||||
if (!isDefined(viewFieldData.workspaceId)) {
|
||||
throw new ViewFieldException(
|
||||
generateViewFieldExceptionMessage(
|
||||
ViewFieldExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
ViewFieldExceptionCode.INVALID_VIEW_FIELD_DATA,
|
||||
{
|
||||
userFriendlyMessage: generateViewFieldUserFriendlyExceptionMessage(
|
||||
ViewFieldExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const existingFlatViewFieldMaps = await this.getExistingFlatViewFieldMaps(
|
||||
viewFieldData.workspaceId,
|
||||
);
|
||||
|
||||
const flatViewFieldFromCreateInput =
|
||||
fromPartialFlatViewFieldToFlatViewFieldWithDefault({
|
||||
...viewFieldData,
|
||||
universalIdentifier: viewFieldData.universalIdentifier ?? v4(),
|
||||
});
|
||||
|
||||
const toFlatViewFieldMaps: FlatViewFieldMaps =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFieldFromCreateInput,
|
||||
flatEntityMaps: existingFlatViewFieldMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
viewField: {
|
||||
fromFlatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId: viewFieldData.workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while creating view field',
|
||||
);
|
||||
}
|
||||
|
||||
const [createdViewField] = await this.viewFieldRepository.find({
|
||||
where: {
|
||||
id: flatViewFieldFromCreateInput.id,
|
||||
},
|
||||
});
|
||||
|
||||
return createdViewField;
|
||||
}
|
||||
|
||||
async updateOne(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
updateData: Partial<ViewFieldEntity>,
|
||||
): Promise<ViewFieldEntity> {
|
||||
const existingFlatViewFieldMaps =
|
||||
await this.getExistingFlatViewFieldMaps(workspaceId);
|
||||
|
||||
const existingViewField = existingFlatViewFieldMaps.byId[id];
|
||||
|
||||
if (!isDefined(existingViewField)) {
|
||||
throw new ViewFieldException(
|
||||
generateViewFieldExceptionMessage(
|
||||
ViewFieldExceptionMessageKey.VIEW_FIELD_NOT_FOUND,
|
||||
id,
|
||||
),
|
||||
ViewFieldExceptionCode.VIEW_FIELD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const existingViewFieldToUpdate = removePropertiesFromRecord(
|
||||
{
|
||||
...existingViewField,
|
||||
...updateData,
|
||||
},
|
||||
VIEW_FIELD_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
const flatViewFieldFromUpdateInput =
|
||||
fromPartialFlatViewFieldToFlatViewFieldWithDefault({
|
||||
...existingViewFieldToUpdate,
|
||||
universalIdentifier:
|
||||
existingViewFieldToUpdate.universalIdentifier ?? '',
|
||||
});
|
||||
|
||||
const toFlatViewFieldMaps: FlatViewFieldMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFieldFromUpdateInput,
|
||||
flatEntityMaps: existingFlatViewFieldMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
viewField: {
|
||||
fromFlatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while updating view field',
|
||||
);
|
||||
}
|
||||
|
||||
const [updatedViewField] = await this.viewFieldRepository.find({
|
||||
where: {
|
||||
id: Equal(id),
|
||||
},
|
||||
});
|
||||
|
||||
return updatedViewField;
|
||||
}
|
||||
|
||||
async deleteOne(id: string, workspaceId: string): Promise<boolean> {
|
||||
const existingFlatViewFieldMaps =
|
||||
await this.getExistingFlatViewFieldMaps(workspaceId);
|
||||
|
||||
const existingViewFieldToDelete = existingFlatViewFieldMaps.byId[id];
|
||||
|
||||
if (!isDefined(existingViewFieldToDelete)) {
|
||||
throw new ViewFieldException(
|
||||
generateViewFieldExceptionMessage(
|
||||
ViewFieldExceptionMessageKey.VIEW_FIELD_NOT_FOUND,
|
||||
id,
|
||||
),
|
||||
ViewFieldExceptionCode.VIEW_FIELD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const toFlatViewFieldMaps: FlatViewFieldMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: existingFlatViewFieldMaps,
|
||||
entityToDeleteId: existingViewFieldToDelete.id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
viewField: {
|
||||
fromFlatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while deleting view field',
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { ViewCacheService } from 'src/engine/core-modules/view/cache/services/view-cache.service';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
ViewExceptionMessageKey,
|
||||
generateViewExceptionMessage,
|
||||
generateViewUserFriendlyExceptionMessage,
|
||||
} from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { VIEW_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/view-entity-relation-properties.constant';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { fromPartialFlatViewToFlatViewWithDefault } from 'src/engine/core-modules/view/flat-view/utils/from-partial-flat-view-to-flat-view-to-with-default.util';
|
||||
import { WorkspaceMigrationOrchestratorException } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-orchestrator-exception';
|
||||
import { WorkspaceMigrationBuildOrchestratorService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service';
|
||||
|
||||
@Injectable()
|
||||
export class ViewV2Service {
|
||||
constructor(
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
private readonly workspaceMigrationOrchestratorService: WorkspaceMigrationBuildOrchestratorService,
|
||||
private readonly viewCacheService: ViewCacheService,
|
||||
) {}
|
||||
|
||||
async createOne(viewData: Partial<ViewEntity>): Promise<ViewEntity> {
|
||||
if (!isDefined(viewData.workspaceId)) {
|
||||
throw new ViewException(
|
||||
generateViewExceptionMessage(
|
||||
ViewExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
{
|
||||
userFriendlyMessage: generateViewUserFriendlyExceptionMessage(
|
||||
ViewExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId: viewData.workspaceId,
|
||||
});
|
||||
|
||||
const flatViewFromCreateInput = fromPartialFlatViewToFlatViewWithDefault({
|
||||
...viewData,
|
||||
universalIdentifier: viewData.universalIdentifier ?? v4(),
|
||||
});
|
||||
|
||||
const toFlatViewMaps: FlatViewMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFromCreateInput,
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
view: {
|
||||
fromFlatViewMaps: existingFlatViewMaps,
|
||||
toFlatViewMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId: viewData.workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while creating view',
|
||||
);
|
||||
}
|
||||
|
||||
const [createdView] = await this.viewRepository.find({
|
||||
where: {
|
||||
id: flatViewFromCreateInput.id,
|
||||
},
|
||||
});
|
||||
|
||||
return createdView;
|
||||
}
|
||||
|
||||
async updateOne(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
updateData: Partial<ViewEntity>,
|
||||
): Promise<ViewEntity> {
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const existingView = existingFlatViewMaps.byId[id];
|
||||
|
||||
if (!isDefined(existingView)) {
|
||||
throw new ViewException(
|
||||
generateViewExceptionMessage(
|
||||
ViewExceptionMessageKey.VIEW_NOT_FOUND,
|
||||
id,
|
||||
),
|
||||
ViewExceptionCode.VIEW_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const existingViewToUpdate = removePropertiesFromRecord(
|
||||
{
|
||||
...existingView,
|
||||
...updateData,
|
||||
},
|
||||
VIEW_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
const flatViewFromUpdateInput = fromPartialFlatViewToFlatViewWithDefault({
|
||||
...existingViewToUpdate,
|
||||
universalIdentifier: existingViewToUpdate.universalIdentifier ?? '',
|
||||
});
|
||||
|
||||
const toFlatViewMaps: FlatViewMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFromUpdateInput,
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
view: {
|
||||
fromFlatViewMaps: existingFlatViewMaps,
|
||||
toFlatViewMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while updating view',
|
||||
);
|
||||
}
|
||||
|
||||
const [updatedView] = await this.viewRepository.find({
|
||||
where: {
|
||||
id: Equal(id),
|
||||
},
|
||||
});
|
||||
|
||||
return updatedView;
|
||||
}
|
||||
|
||||
async deleteOne(id: string, workspaceId: string): Promise<boolean> {
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const existingViewToDelete = existingFlatViewMaps.byId[id];
|
||||
|
||||
if (!isDefined(existingViewToDelete)) {
|
||||
throw new ViewException(
|
||||
generateViewExceptionMessage(
|
||||
ViewExceptionMessageKey.VIEW_NOT_FOUND,
|
||||
id,
|
||||
),
|
||||
ViewExceptionCode.VIEW_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const toFlatViewMaps: FlatViewMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
entityToDeleteId: existingViewToDelete.id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationOrchestratorService.buildWorkspaceMigrations(
|
||||
{
|
||||
entityMaps: {
|
||||
view: {
|
||||
fromFlatViewMaps: existingFlatViewMaps,
|
||||
toFlatViewMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Multiple validation errors occurred while deleting view',
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { I18nModule } from 'src/engine/core-modules/i18n/i18n.module';
|
||||
import { ViewCacheModule } from 'src/engine/core-modules/view/cache/services/view-cache.module';
|
||||
import { ViewFieldController } from 'src/engine/core-modules/view/controllers/view-field.controller';
|
||||
import { ViewFilterGroupController } from 'src/engine/core-modules/view/controllers/view-filter-group.controller';
|
||||
import { ViewFilterController } from 'src/engine/core-modules/view/controllers/view-filter.controller';
|
||||
@@ -20,14 +21,17 @@ import { ViewFilterResolver } from 'src/engine/core-modules/view/resolvers/view-
|
||||
import { ViewGroupResolver } from 'src/engine/core-modules/view/resolvers/view-group.resolver';
|
||||
import { ViewSortResolver } from 'src/engine/core-modules/view/resolvers/view-sort.resolver';
|
||||
import { ViewResolver } from 'src/engine/core-modules/view/resolvers/view.resolver';
|
||||
import { ViewFieldV2Service } from 'src/engine/core-modules/view/services/view-field-v2.service';
|
||||
import { ViewFieldService } from 'src/engine/core-modules/view/services/view-field.service';
|
||||
import { ViewFilterGroupService } from 'src/engine/core-modules/view/services/view-filter-group.service';
|
||||
import { ViewFilterService } from 'src/engine/core-modules/view/services/view-filter.service';
|
||||
import { ViewGroupService } from 'src/engine/core-modules/view/services/view-group.service';
|
||||
import { ViewSortService } from 'src/engine/core-modules/view/services/view-sort.service';
|
||||
import { ViewV2Service } from 'src/engine/core-modules/view/services/view-v2.service';
|
||||
import { ViewService } from 'src/engine/core-modules/view/services/view.service';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -42,6 +46,8 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
I18nModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
ViewCacheModule,
|
||||
],
|
||||
controllers: [
|
||||
ViewController,
|
||||
@@ -64,6 +70,8 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
ViewFilterGroupResolver,
|
||||
ViewGroupResolver,
|
||||
ViewSortResolver,
|
||||
ViewV2Service,
|
||||
ViewFieldV2Service,
|
||||
],
|
||||
exports: [
|
||||
ViewService,
|
||||
@@ -72,6 +80,8 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
ViewFilterGroupService,
|
||||
ViewGroupService,
|
||||
ViewSortService,
|
||||
ViewV2Service,
|
||||
ViewFieldV2Service,
|
||||
],
|
||||
})
|
||||
export class CoreViewModule {}
|
||||
|
||||
+3
-3
@@ -105,7 +105,7 @@ export class FieldMetadataServiceV2 {
|
||||
{
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
},
|
||||
fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject,
|
||||
toFlatObjectMetadataMaps,
|
||||
@@ -181,7 +181,7 @@ export class FieldMetadataServiceV2 {
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
@@ -270,7 +270,7 @@ export class FieldMetadataServiceV2 {
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
|
||||
+5
-24
@@ -5,7 +5,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewV2Service } from 'src/engine/core-modules/view/services/view-v2.service';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
@@ -42,6 +42,7 @@ export class ObjectMetadataServiceV2 {
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
private readonly viewV2Service: ViewV2Service,
|
||||
) {}
|
||||
|
||||
async updateOne({
|
||||
@@ -96,7 +97,7 @@ export class ObjectMetadataServiceV2 {
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
@@ -198,7 +199,7 @@ export class ObjectMetadataServiceV2 {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
workspaceId,
|
||||
@@ -288,7 +289,7 @@ export class ObjectMetadataServiceV2 {
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
@@ -318,26 +319,6 @@ export class ObjectMetadataServiceV2 {
|
||||
);
|
||||
}
|
||||
|
||||
const [view] = await this.viewRepository.find({
|
||||
where: {
|
||||
objectMetadataId: createdFlatObjectMetadata.id,
|
||||
key: ViewKey.INDEX,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(view)) {
|
||||
throw new ObjectMetadataException(
|
||||
'View not created',
|
||||
ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
await this.objectMetadataRelatedRecordsService.createViewWorkspaceFavorite(
|
||||
workspaceId,
|
||||
view.id,
|
||||
);
|
||||
|
||||
return createdFlatObjectMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export class WorkspaceMigrationOrchestratorException extends Error {
|
||||
constructor(message = 'Workspace migration orchestrator failed') {
|
||||
super(message);
|
||||
this.name = 'WorkspaceMigrationOrchestratorException';
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
|
||||
import { ViewCacheService } from 'src/engine/core-modules/view/cache/services/view-cache.service';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationOrchestratorException } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-orchestrator-exception';
|
||||
import {
|
||||
WorkspaceMigrationOrchestratorBuildArgs,
|
||||
WorkspaceMigrationOrchestratorEntityMaps,
|
||||
WorkspaceMigrationOrchestratorFailedResult,
|
||||
WorkspaceMigrationOrchestratorOptimisticEntityMaps,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceViewFieldMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-field-migration-builder-v2.service';
|
||||
import { WorkspaceViewMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/services/workspace-migration-runner-v2.service';
|
||||
import {
|
||||
WorkspaceMigrationV2Exception,
|
||||
WorkspaceMigrationV2ExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration.exception';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuildOrchestratorService {
|
||||
private readonly logger = new Logger(
|
||||
WorkspaceMigrationBuildOrchestratorService.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceMigrationBuilderV2Service: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceViewMigrationBuilderV2Service: WorkspaceViewMigrationBuilderV2Service,
|
||||
private readonly workspaceViewFieldMigrationBuilderV2Service: WorkspaceViewFieldMigrationBuilderV2Service,
|
||||
private readonly migrationRunner: WorkspaceMigrationRunnerV2Service,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly viewCacheService: ViewCacheService,
|
||||
) {}
|
||||
|
||||
public async buildWorkspaceMigrations({
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
entityMaps,
|
||||
}: WorkspaceMigrationOrchestratorBuildArgs): Promise<
|
||||
WorkspaceMigrationOrchestratorFailedResult | undefined
|
||||
> {
|
||||
try {
|
||||
const allObjectAndFieldActions = [];
|
||||
const allViewActions = [];
|
||||
const allViewFieldActions = [];
|
||||
const allFailures = [];
|
||||
|
||||
const optimisticEntityMaps = await this.prepareOptimisticEntityMaps({
|
||||
entityMaps,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (entityMaps.object) {
|
||||
const objectResult =
|
||||
await this.workspaceMigrationBuilderV2Service.validateAndBuild({
|
||||
fromFlatObjectMetadataMaps:
|
||||
entityMaps.object.fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps:
|
||||
entityMaps.object.toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
if (objectResult.status === 'fail') {
|
||||
allFailures.push(...objectResult.errors);
|
||||
} else {
|
||||
optimisticEntityMaps.object = objectResult.optimisticFlatEntityMaps;
|
||||
allObjectAndFieldActions.push(
|
||||
...objectResult.workspaceMigration.actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (entityMaps.view) {
|
||||
const viewResult =
|
||||
await this.workspaceViewMigrationBuilderV2Service.validateAndBuild({
|
||||
fromFlatViewMaps: entityMaps.view.fromFlatViewMaps,
|
||||
toFlatViewMaps: entityMaps.view.toFlatViewMaps,
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
dependencyOptimisticEntityMaps: {
|
||||
object: optimisticEntityMaps.object,
|
||||
},
|
||||
});
|
||||
|
||||
if (viewResult.status === 'fail') {
|
||||
allFailures.push(...viewResult.errors);
|
||||
} else {
|
||||
optimisticEntityMaps.view = viewResult.optimisticFlatEntityMaps;
|
||||
allViewActions.push(...viewResult.workspaceMigration.actions);
|
||||
}
|
||||
}
|
||||
|
||||
if (entityMaps.viewField) {
|
||||
const viewFieldResult =
|
||||
await this.workspaceViewFieldMigrationBuilderV2Service.validateAndBuild(
|
||||
{
|
||||
fromFlatViewFieldMaps: entityMaps.viewField.fromFlatViewFieldMaps,
|
||||
toFlatViewFieldMaps: entityMaps.viewField.toFlatViewFieldMaps,
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
dependencyOptimisticEntityMaps: {
|
||||
object: optimisticEntityMaps.object,
|
||||
view: optimisticEntityMaps.view,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (viewFieldResult.status === 'fail') {
|
||||
allFailures.push(...viewFieldResult.errors);
|
||||
} else {
|
||||
optimisticEntityMaps.viewField =
|
||||
viewFieldResult.optimisticFlatEntityMaps;
|
||||
allViewFieldActions.push(
|
||||
...viewFieldResult.workspaceMigration.actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (allFailures.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: allFailures,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: return workspace migrations
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
throw new WorkspaceMigrationV2Exception(
|
||||
WorkspaceMigrationV2ExceptionCode.BUILDER_INTERNAL_SERVER_ERROR,
|
||||
error.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to dedicated service
|
||||
private async prepareOptimisticEntityMaps({
|
||||
entityMaps,
|
||||
workspaceId,
|
||||
}: {
|
||||
entityMaps: WorkspaceMigrationOrchestratorEntityMaps;
|
||||
workspaceId: string;
|
||||
}): Promise<WorkspaceMigrationOrchestratorOptimisticEntityMaps> {
|
||||
const optimisticObjectMaps =
|
||||
entityMaps.object?.fromFlatObjectMetadataMaps ??
|
||||
(
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
)
|
||||
).flatObjectMetadataMaps;
|
||||
|
||||
if (!isDefined(optimisticObjectMaps)) {
|
||||
throw new WorkspaceMigrationOrchestratorException(
|
||||
'Object metadata maps not found',
|
||||
);
|
||||
}
|
||||
|
||||
const optimisticViewMaps =
|
||||
entityMaps.view?.fromFlatViewMaps ??
|
||||
(
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
})
|
||||
).flatViewMaps;
|
||||
|
||||
if (!isDefined(optimisticViewMaps)) {
|
||||
throw new WorkspaceMigrationOrchestratorException('View maps not found');
|
||||
}
|
||||
|
||||
// For ViewFields, if not provided, start with empty maps since ViewFields are optional
|
||||
const optimisticViewFieldMaps = entityMaps.viewField
|
||||
?.fromFlatViewFieldMaps ?? {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
return {
|
||||
object: optimisticObjectMaps,
|
||||
view: optimisticViewMaps,
|
||||
viewField: optimisticViewFieldMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
+1
@@ -11,6 +11,7 @@ import {
|
||||
WorkspaceMigrationV2ExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration.exception';
|
||||
|
||||
// TODO: replace build logic by orchestrator service
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
private readonly logger = new Logger(
|
||||
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
|
||||
export type FlatViewField = Pick<
|
||||
ViewFieldEntity,
|
||||
'fieldMetadataId' | 'position' | 'isVisible' | 'size'
|
||||
>;
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type FlatViewField } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-view-field.type';
|
||||
|
||||
export type FlatView = Pick<
|
||||
ViewEntity,
|
||||
'name' | 'objectMetadataId' | 'type' | 'isCustom' | 'key' | 'icon'
|
||||
> & {
|
||||
viewFields: FlatViewField[];
|
||||
};
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FailedFlatViewFieldValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-field-validation.type';
|
||||
import { type FailedFlatViewValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation.type';
|
||||
import { type FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { type FailedFlatFieldMetadataValidation } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FailedFlatObjectMetadataValidation } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { type WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
|
||||
export type WorkspaceMigrationOrchestratorEntityMaps = {
|
||||
object?: FromTo<FlatObjectMetadataMaps, 'FlatObjectMetadataMaps'>;
|
||||
field?: FromTo<FlatObjectMetadataMaps, 'FlatObjectMetadataMaps'>; // Fields are part of object maps for now
|
||||
view?: FromTo<FlatViewMaps, 'FlatViewMaps'>;
|
||||
viewField?: FromTo<FlatViewFieldMaps, 'FlatViewFieldMaps'>;
|
||||
};
|
||||
|
||||
type UnwrapFromTo<T> = T extends FromTo<infer U, infer _Tag> ? U : T;
|
||||
|
||||
export type WorkspaceMigrationOrchestratorOptimisticEntityMaps = {
|
||||
[K in keyof WorkspaceMigrationOrchestratorEntityMaps]?: UnwrapFromTo<
|
||||
WorkspaceMigrationOrchestratorEntityMaps[K]
|
||||
>;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationOrchestratorBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
entityMaps: WorkspaceMigrationOrchestratorEntityMaps;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationOrchestratorFailedResult = {
|
||||
status: 'fail';
|
||||
errors: (
|
||||
| FailedFlatObjectMetadataValidation
|
||||
| FailedFlatFieldMetadataValidation
|
||||
| FailedFlatViewValidation
|
||||
| FailedFlatViewFieldValidation
|
||||
)[];
|
||||
};
|
||||
-333
@@ -1033,85 +1033,6 @@ exports[`Workspace migration builder object actions test suite It should build a
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"type": "create_view",
|
||||
"view": {
|
||||
"icon": "IconList",
|
||||
"isCustom": false,
|
||||
"key": "INDEX",
|
||||
"name": "All {objectLabelPlural}",
|
||||
"objectMetadataId": Any<String>,
|
||||
"type": "TABLE",
|
||||
"viewFields": [
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 0,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 1,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 2,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 3,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 4,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 5,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 6,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 7,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 8,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 9,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 10,
|
||||
"size": 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -2419,181 +2340,6 @@ exports[`Workspace migration builder object actions test suite It should build a
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"type": "create_view",
|
||||
"view": {
|
||||
"icon": "IconList",
|
||||
"isCustom": false,
|
||||
"key": "INDEX",
|
||||
"name": "All {objectLabelPlural}",
|
||||
"objectMetadataId": Any<String>,
|
||||
"type": "TABLE",
|
||||
"viewFields": [
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 0,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 1,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 2,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 3,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 4,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 5,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 6,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 7,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 8,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 9,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 10,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 11,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 12,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 13,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 14,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 15,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 16,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 17,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 18,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 19,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 20,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 21,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 22,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 23,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 24,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 25,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 26,
|
||||
"size": 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -3246,85 +2992,6 @@ exports[`Workspace migration builder object actions test suite It should build a
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
{
|
||||
"type": "create_view",
|
||||
"view": {
|
||||
"icon": "IconList",
|
||||
"isCustom": false,
|
||||
"key": "INDEX",
|
||||
"name": "All {objectLabelPlural}",
|
||||
"objectMetadataId": Any<String>,
|
||||
"type": "TABLE",
|
||||
"viewFields": [
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 0,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 1,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 2,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 3,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 4,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 5,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 6,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 7,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 8,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 9,
|
||||
"size": 180,
|
||||
},
|
||||
{
|
||||
"fieldMetadataId": Any<String>,
|
||||
"isVisible": true,
|
||||
"position": 10,
|
||||
"size": 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
|
||||
+1
-4
@@ -44,7 +44,7 @@ const DELETE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
context: {
|
||||
input: {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
isSystemBuild: true,
|
||||
},
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
@@ -81,7 +81,6 @@ const CREATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
createView: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -103,7 +102,6 @@ const CREATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
createView: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -114,7 +112,6 @@ const CREATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
createObject: 1,
|
||||
createView: 1,
|
||||
},
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
|
||||
+1
-2
@@ -54,7 +54,6 @@ const expectedActionsTypeCounterChecker = ({
|
||||
deleteObject: 0,
|
||||
updateField: 0,
|
||||
updateObject: 0,
|
||||
createView: 0,
|
||||
};
|
||||
const actualActionsTypeCounter = workspaceMigration.actions.reduce(
|
||||
(acc, action) => {
|
||||
@@ -125,7 +124,7 @@ describe.each(allWorkspaceBuilderTestCases)(
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions = {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
} = typeof input === 'function' ? input() : input;
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ export class WorkspaceMigrationV2FieldActionsBuilderService {
|
||||
validateAndBuildResult.created.push(createFieldAction);
|
||||
}
|
||||
|
||||
for (const flatFieldMetadataToDelete of buildOptions.inferDeletionFromMissingObjectFieldIndex
|
||||
for (const flatFieldMetadataToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatFieldMetadatas
|
||||
: []) {
|
||||
const validationErrors =
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService {
|
||||
validateAndBuildResult.created.push(createObjectAction);
|
||||
}
|
||||
|
||||
for (const flatObjectMetadataToDelete of buildOptions.inferDeletionFromMissingObjectFieldIndex
|
||||
for (const flatObjectMetadataToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatObjectMetadatas
|
||||
: []) {
|
||||
const validationErrors =
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FailedFlatViewFieldValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-field-validation.type';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { compareTwoFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/compare-two-flat-view-field.util';
|
||||
import { WorkspaceMigrationOrchestratorOptimisticEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { type CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceViewFieldMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-field-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import {
|
||||
UpdateViewFieldAction,
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import {
|
||||
getWorkspaceMigrationV2ViewFieldCreateAction,
|
||||
getWorkspaceMigrationV2ViewFieldDeleteAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-view-field-action';
|
||||
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
|
||||
|
||||
export type CreatedDeletedUpdatedViewFieldInputMatrix = FromTo<
|
||||
FlatViewFieldMaps,
|
||||
'FlatViewFieldMaps'
|
||||
> &
|
||||
CustomDeletedCreatedUpdatedMatrix<'flatViewField', FlatViewField> & {
|
||||
buildOptions: WorkspaceViewFieldMigrationV2BuilderOptions;
|
||||
fromFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
dependencyOptimisticEntityMaps: WorkspaceMigrationOrchestratorOptimisticEntityMaps;
|
||||
};
|
||||
|
||||
type ValidateAndBuildViewFieldResult<T extends WorkspaceMigrationActionV2> = {
|
||||
failed: FailedFlatViewFieldValidation[];
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: T[];
|
||||
optimisticFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewFieldActionsBuilderService {
|
||||
constructor(
|
||||
private readonly flatViewFieldValidatorService: FlatViewFieldValidatorService,
|
||||
) {}
|
||||
|
||||
public async validateAndBuildViewFieldActions({
|
||||
createdFlatViewField,
|
||||
deletedFlatViewField,
|
||||
updatedFlatViewField,
|
||||
buildOptions,
|
||||
fromFlatViewFieldMaps,
|
||||
dependencyOptimisticEntityMaps,
|
||||
}: CreatedDeletedUpdatedViewFieldInputMatrix): Promise<
|
||||
ValidateAndBuildViewFieldResult<WorkspaceMigrationViewFieldActionV2>
|
||||
> {
|
||||
const validateAndBuildResult: ValidateAndBuildViewFieldResult<WorkspaceMigrationViewFieldActionV2> =
|
||||
{
|
||||
failed: [],
|
||||
created: [],
|
||||
deleted: [],
|
||||
updated: [],
|
||||
optimisticFlatViewFieldMaps: structuredClone(fromFlatViewFieldMaps),
|
||||
};
|
||||
|
||||
if (
|
||||
!isDefined(dependencyOptimisticEntityMaps.object) ||
|
||||
!isDefined(dependencyOptimisticEntityMaps.view)
|
||||
) {
|
||||
throw new Error('Dependency optimistic entity maps are not defined');
|
||||
}
|
||||
|
||||
for (const flatViewFieldToCreate of createdFlatViewField) {
|
||||
const validationErrors =
|
||||
await this.flatViewFieldValidatorService.validateFlatViewFieldCreation({
|
||||
_existingFlatViewFieldMaps:
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
flatViewFieldToValidate: flatViewFieldToCreate,
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
dependencyOptimisticEntityMaps.object,
|
||||
optimisticFlatViewMaps: dependencyOptimisticEntityMaps.view,
|
||||
});
|
||||
|
||||
if (validationErrors.viewFieldLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFieldToCreate,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
});
|
||||
|
||||
const createViewFieldAction =
|
||||
getWorkspaceMigrationV2ViewFieldCreateAction({
|
||||
flatViewField: flatViewFieldToCreate,
|
||||
});
|
||||
|
||||
validateAndBuildResult.created.push(createViewFieldAction);
|
||||
}
|
||||
|
||||
for (const flatViewFieldToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatViewField
|
||||
: []) {
|
||||
const validationErrors =
|
||||
this.flatViewFieldValidatorService.validateFlatViewFieldDeletion({
|
||||
existingFlatViewFieldMaps:
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
viewFieldIdToDelete: flatViewFieldToDelete.id,
|
||||
optimisticFlatViewMaps: dependencyOptimisticEntityMaps.view,
|
||||
});
|
||||
|
||||
if (validationErrors.viewFieldLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatViewFieldToDelete.id,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
});
|
||||
|
||||
const deleteViewFieldAction =
|
||||
getWorkspaceMigrationV2ViewFieldDeleteAction(flatViewFieldToDelete);
|
||||
|
||||
validateAndBuildResult.deleted.push(deleteViewFieldAction);
|
||||
}
|
||||
|
||||
for (const {
|
||||
from: fromFlatViewField,
|
||||
to: toFlatViewField,
|
||||
} of updatedFlatViewField) {
|
||||
const viewFieldUpdatedProperties = compareTwoFlatViewField({
|
||||
fromFlatViewField,
|
||||
toFlatViewField,
|
||||
});
|
||||
|
||||
if (viewFieldUpdatedProperties.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validationErrors =
|
||||
this.flatViewFieldValidatorService.validateFlatViewFieldUpdate({
|
||||
existingFlatViewFieldMaps:
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
updatedFlatViewField: toFlatViewField,
|
||||
optimisticFlatViewMaps: dependencyOptimisticEntityMaps.view,
|
||||
});
|
||||
|
||||
if (validationErrors.viewFieldLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewFieldMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: toFlatViewField,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
});
|
||||
|
||||
const updateViewFieldAction: UpdateViewFieldAction = {
|
||||
type: 'update_view_field',
|
||||
viewFieldId: toFlatViewField.id,
|
||||
updates: viewFieldUpdatedProperties,
|
||||
};
|
||||
|
||||
validateAndBuildResult.updated.push(updateViewFieldAction);
|
||||
}
|
||||
|
||||
return validateAndBuildResult;
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FailedFlatViewValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation.type';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { compareTwoFlatView } from 'src/engine/core-modules/view/flat-view/utils/compare-two-flat-view.util';
|
||||
import { WorkspaceMigrationOrchestratorOptimisticEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { type CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceViewMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import {
|
||||
UpdateViewAction,
|
||||
WorkspaceMigrationViewActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import {
|
||||
getWorkspaceMigrationV2ViewCreateAction,
|
||||
getWorkspaceMigrationV2ViewDeleteAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-view-action';
|
||||
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
|
||||
|
||||
export type CreatedDeletedUpdatedViewInputMatrix = FromTo<
|
||||
FlatViewMaps,
|
||||
'FlatViewMaps'
|
||||
> &
|
||||
CustomDeletedCreatedUpdatedMatrix<'flatView', FlatView> & {
|
||||
buildOptions: WorkspaceViewMigrationV2BuilderOptions;
|
||||
fromFlatViewMaps: FlatViewMaps;
|
||||
dependencyOptimisticEntityMaps: WorkspaceMigrationOrchestratorOptimisticEntityMaps;
|
||||
};
|
||||
|
||||
type ValidateAndBuildViewResult<T extends WorkspaceMigrationActionV2> = {
|
||||
failed: FailedFlatViewValidation[];
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: T[];
|
||||
optimisticFlatViewMaps: FlatViewMaps;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewActionsBuilderService {
|
||||
constructor(
|
||||
private readonly flatViewValidatorService: FlatViewValidatorService,
|
||||
) {}
|
||||
|
||||
public async validateAndBuildViewActions({
|
||||
createdFlatView,
|
||||
deletedFlatView,
|
||||
updatedFlatView,
|
||||
buildOptions,
|
||||
fromFlatViewMaps,
|
||||
dependencyOptimisticEntityMaps,
|
||||
}: CreatedDeletedUpdatedViewInputMatrix): Promise<
|
||||
ValidateAndBuildViewResult<WorkspaceMigrationViewActionV2>
|
||||
> {
|
||||
const validateAndBuildResult: ValidateAndBuildViewResult<WorkspaceMigrationViewActionV2> =
|
||||
{
|
||||
failed: [],
|
||||
created: [],
|
||||
deleted: [],
|
||||
updated: [],
|
||||
optimisticFlatViewMaps: structuredClone(fromFlatViewMaps),
|
||||
};
|
||||
|
||||
if (!isDefined(dependencyOptimisticEntityMaps.object)) {
|
||||
throw new Error('Dependency optimistic entity maps are not defined');
|
||||
}
|
||||
|
||||
for (const flatViewToCreate of createdFlatView) {
|
||||
const validationErrors =
|
||||
await this.flatViewValidatorService.validateFlatViewCreation({
|
||||
_existingFlatViewMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
flatViewToValidate: flatViewToCreate,
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
dependencyOptimisticEntityMaps.object,
|
||||
});
|
||||
|
||||
if (validationErrors.viewLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewMaps =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewToCreate,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
});
|
||||
|
||||
const createViewAction = getWorkspaceMigrationV2ViewCreateAction({
|
||||
flatView: flatViewToCreate,
|
||||
});
|
||||
|
||||
validateAndBuildResult.created.push(createViewAction);
|
||||
}
|
||||
|
||||
for (const flatViewToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatView
|
||||
: []) {
|
||||
const validationErrors =
|
||||
this.flatViewValidatorService.validateFlatViewDeletion({
|
||||
existingFlatViewMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
viewIdToDelete: flatViewToDelete.id,
|
||||
});
|
||||
|
||||
if (validationErrors.viewLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatViewToDelete.id,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
});
|
||||
|
||||
const deleteViewAction =
|
||||
getWorkspaceMigrationV2ViewDeleteAction(flatViewToDelete);
|
||||
|
||||
validateAndBuildResult.deleted.push(deleteViewAction);
|
||||
}
|
||||
|
||||
for (const { from: fromFlatView, to: toFlatView } of updatedFlatView) {
|
||||
const viewUpdatedProperties = compareTwoFlatView({
|
||||
fromFlatView,
|
||||
toFlatView,
|
||||
});
|
||||
|
||||
if (viewUpdatedProperties.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validationErrors =
|
||||
this.flatViewValidatorService.validateFlatViewUpdate({
|
||||
existingFlatViewMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
updatedFlatView: toFlatView,
|
||||
});
|
||||
|
||||
if (validationErrors.viewLevelErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatViewMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: toFlatView,
|
||||
flatEntityMaps: validateAndBuildResult.optimisticFlatViewMaps,
|
||||
});
|
||||
|
||||
const updateViewAction: UpdateViewAction = {
|
||||
type: 'update_view',
|
||||
viewId: toFlatView.id,
|
||||
updates: viewUpdatedProperties,
|
||||
};
|
||||
|
||||
validateAndBuildResult.updated.push(updateViewAction);
|
||||
}
|
||||
|
||||
return validateAndBuildResult;
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
type CreateViewAction,
|
||||
type WorkspaceMigrationViewActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
|
||||
type BuildWorkspaceMigrationViewActionsArgs = {
|
||||
createdFlatObjectMetadatas: FlatObjectMetadata[];
|
||||
};
|
||||
|
||||
const getCreateViewAction = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): CreateViewAction => {
|
||||
return {
|
||||
type: 'create_view',
|
||||
view: {
|
||||
objectMetadataId: flatObjectMetadata.id,
|
||||
type: ViewType.TABLE,
|
||||
name: `All {objectLabelPlural}`,
|
||||
key: ViewKey.INDEX,
|
||||
isCustom: false,
|
||||
icon: 'IconList',
|
||||
viewFields: flatObjectMetadata.flatFieldMetadatas
|
||||
.filter((field) => field.name !== 'id' && field.name !== 'deletedAt')
|
||||
.map((field, index) => ({
|
||||
fieldMetadataId: field.id,
|
||||
position: index,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
})),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const buildWorkspaceMigrationViewActions = ({
|
||||
createdFlatObjectMetadatas,
|
||||
}: BuildWorkspaceMigrationViewActionsArgs): WorkspaceMigrationViewActionV2[] => {
|
||||
return createdFlatObjectMetadatas.map(getCreateViewAction);
|
||||
};
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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';
|
||||
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { type WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
|
||||
export type SuccessfulEntityMigrationBuildResult<TFlatEntityMaps> = {
|
||||
status: 'success';
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
optimisticFlatEntityMaps: TFlatEntityMaps;
|
||||
};
|
||||
|
||||
export type FailedEntityMigrationBuildResult<TFailedValidation> = {
|
||||
status: 'fail';
|
||||
errors: TFailedValidation[];
|
||||
};
|
||||
|
||||
export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
TFlatEntityMaps extends FlatEntityMaps<TFlatEntity>,
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFailedValidation,
|
||||
TActionType extends WorkspaceMigrationActionV2,
|
||||
TBuildArgs extends { workspaceId: string } & FromTo<TFlatEntityMaps, string>,
|
||||
> {
|
||||
public async validateAndBuild(
|
||||
args: TBuildArgs,
|
||||
): Promise<
|
||||
| SuccessfulEntityMigrationBuildResult<TFlatEntityMaps>
|
||||
| FailedEntityMigrationBuildResult<TFailedValidation>
|
||||
> {
|
||||
const { from: fromEntityMaps, to: toEntityMaps } =
|
||||
this.extractFromToEntityMaps(args);
|
||||
|
||||
const fromEntities = Object.values(fromEntityMaps.byId).filter(isDefined);
|
||||
const toEntities = Object.values(toEntityMaps.byId).filter(isDefined);
|
||||
|
||||
const { created, deleted, updated } = deletedCreatedUpdatedMatrixDispatcher(
|
||||
{
|
||||
from: fromEntities,
|
||||
to: toEntities,
|
||||
},
|
||||
);
|
||||
|
||||
const result = await this.validateAndBuildActions({
|
||||
...args,
|
||||
created,
|
||||
updated,
|
||||
deleted,
|
||||
});
|
||||
|
||||
if (result.failed.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: result.failed,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
workspaceMigration: {
|
||||
workspaceId: args.workspaceId,
|
||||
actions: [...result.deleted, ...result.created, ...result.updated],
|
||||
},
|
||||
optimisticFlatEntityMaps: result.optimisticMaps,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Refactor, this is too hacky
|
||||
private extractFromToEntityMaps(args: TBuildArgs): {
|
||||
from: TFlatEntityMaps;
|
||||
to: TFlatEntityMaps;
|
||||
} {
|
||||
const fromKey = Object.keys(args).find((key) =>
|
||||
key.startsWith('from'),
|
||||
) as keyof TBuildArgs;
|
||||
const toKey = Object.keys(args).find((key) =>
|
||||
key.startsWith('to'),
|
||||
) as keyof TBuildArgs;
|
||||
|
||||
return {
|
||||
from: args[fromKey] as TFlatEntityMaps,
|
||||
to: args[toKey] as TFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract validateAndBuildActions(
|
||||
args: TBuildArgs & {
|
||||
created: TFlatEntity[];
|
||||
updated: { from: TFlatEntity; to: TFlatEntity }[];
|
||||
deleted: TFlatEntity[];
|
||||
},
|
||||
): Promise<{
|
||||
failed: TFailedValidation[];
|
||||
created: TActionType[];
|
||||
deleted: TActionType[];
|
||||
updated: TActionType[];
|
||||
optimisticMaps: TFlatEntityMaps;
|
||||
}>;
|
||||
}
|
||||
+11
-13
@@ -10,20 +10,20 @@ import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-mana
|
||||
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 { buildWorkspaceMigrationViewActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder';
|
||||
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 = {
|
||||
inferDeletionFromMissingObjectFieldIndex: boolean;
|
||||
inferDeletionFromMissingEntities: boolean;
|
||||
isSystemBuild: boolean;
|
||||
};
|
||||
|
||||
type SuccessfulWorkspaceMigrationBuildResult = {
|
||||
status: 'success';
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
optimisticFlatEntityMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
|
||||
export type FailedWorkspaceMigrationBuildResult = {
|
||||
@@ -71,11 +71,13 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
to: toFlatObjectMetadatas,
|
||||
});
|
||||
|
||||
// Should be handled separately from objects
|
||||
const objectMetadataDeletedCreatedUpdatedIndex =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix(
|
||||
updatedFlatObjectMetadatas,
|
||||
);
|
||||
|
||||
// Should be handled separately from objects
|
||||
const objectMetadataDeletedCreatedUpdatedFields =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||
updatedFlatObjectMetadatas,
|
||||
@@ -116,12 +118,7 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
const indexWorkspaceMigrationActions = buildWorkspaceMigrationIndexActions({
|
||||
objectMetadataDeletedCreatedUpdatedIndex,
|
||||
inferDeletionFromMissingObjectFieldIndex:
|
||||
buildOptions.inferDeletionFromMissingObjectFieldIndex,
|
||||
});
|
||||
|
||||
// TODO: Remove this from here when we have a proper builder that handles views diffing independently
|
||||
const viewWorkspaceMigrationActions = buildWorkspaceMigrationViewActions({
|
||||
createdFlatObjectMetadatas, // should be flatViews, not objects.
|
||||
buildOptions.inferDeletionFromMissingEntities,
|
||||
});
|
||||
|
||||
const allValidateAndBuildResultFailures = [
|
||||
@@ -141,17 +138,18 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
workspaceMigration: {
|
||||
workspaceId,
|
||||
actions: [
|
||||
...fieldActionsValidateAndBuildResult.deleted,
|
||||
...fieldActionsValidateAndBuildResult.deleted, // Should be handled separately from objects
|
||||
...objectActionsValidateAndBuildResult.deleted,
|
||||
...objectActionsValidateAndBuildResult.created,
|
||||
...objectActionsValidateAndBuildResult.updated,
|
||||
...fieldActionsValidateAndBuildResult.created,
|
||||
...fieldActionsValidateAndBuildResult.updated,
|
||||
...fieldActionsValidateAndBuildResult.created, // Should be handled separately from objects
|
||||
...fieldActionsValidateAndBuildResult.updated, // Should be handled separately from objects
|
||||
...createdObjectMetadataCreateIndexActions,
|
||||
...indexWorkspaceMigrationActions,
|
||||
...viewWorkspaceMigrationActions,
|
||||
...indexWorkspaceMigrationActions, // Should be handled separately from objects
|
||||
],
|
||||
},
|
||||
optimisticFlatEntityMaps:
|
||||
fieldActionsValidateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FailedFlatViewFieldValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-field-validation.type';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { WorkspaceMigrationOrchestratorOptimisticEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
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 {
|
||||
FailedEntityMigrationBuildResult,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationViewFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
|
||||
export type WorkspaceViewFieldMigrationV2BuilderOptions = {
|
||||
isSystemBuild: boolean;
|
||||
inferDeletionFromMissingEntities: boolean;
|
||||
};
|
||||
|
||||
export type FailedWorkspaceViewFieldMigrationBuildResult =
|
||||
FailedEntityMigrationBuildResult<FailedFlatViewFieldValidation>;
|
||||
|
||||
export type WorkspaceViewFieldMigrationBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceViewFieldMigrationV2BuilderOptions;
|
||||
dependencyOptimisticEntityMaps: WorkspaceMigrationOrchestratorOptimisticEntityMaps;
|
||||
} & FromTo<FlatViewFieldMaps, 'FlatViewFieldMaps'>;
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceViewFieldMigrationBuilderV2Service extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
FlatViewFieldMaps,
|
||||
FlatViewField,
|
||||
FailedFlatViewFieldValidation,
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
WorkspaceViewFieldMigrationBuildArgs
|
||||
> {
|
||||
constructor(
|
||||
private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async validateAndBuildActions(
|
||||
args: WorkspaceViewFieldMigrationBuildArgs & {
|
||||
created: FlatViewField[];
|
||||
updated: { from: FlatViewField; to: FlatViewField }[];
|
||||
deleted: FlatViewField[];
|
||||
},
|
||||
): Promise<{
|
||||
failed: FailedFlatViewFieldValidation[];
|
||||
created: WorkspaceMigrationViewFieldActionV2[];
|
||||
deleted: WorkspaceMigrationViewFieldActionV2[];
|
||||
updated: WorkspaceMigrationViewFieldActionV2[];
|
||||
optimisticMaps: FlatViewFieldMaps;
|
||||
}> {
|
||||
const viewFieldActionsValidateAndBuildResult =
|
||||
await this.workspaceMigrationV2ViewFieldActionsBuilderService.validateAndBuildViewFieldActions(
|
||||
{
|
||||
fromFlatViewFieldMaps: args.fromFlatViewFieldMaps,
|
||||
toFlatViewFieldMaps: args.toFlatViewFieldMaps,
|
||||
createdFlatViewField: args.created,
|
||||
updatedFlatViewField: args.updated,
|
||||
deletedFlatViewField: args.deleted,
|
||||
buildOptions: args.buildOptions,
|
||||
dependencyOptimisticEntityMaps: args.dependencyOptimisticEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
failed: viewFieldActionsValidateAndBuildResult.failed,
|
||||
created: viewFieldActionsValidateAndBuildResult.created,
|
||||
deleted: viewFieldActionsValidateAndBuildResult.deleted,
|
||||
updated: viewFieldActionsValidateAndBuildResult.updated,
|
||||
optimisticMaps:
|
||||
viewFieldActionsValidateAndBuildResult.optimisticFlatViewFieldMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FailedFlatViewValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation.type';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { WorkspaceMigrationOrchestratorOptimisticEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
import {
|
||||
FailedEntityMigrationBuildResult,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationViewActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
|
||||
export type WorkspaceViewMigrationV2BuilderOptions = {
|
||||
isSystemBuild: boolean;
|
||||
inferDeletionFromMissingEntities: boolean;
|
||||
};
|
||||
|
||||
export type FailedWorkspaceViewMigrationBuildResult =
|
||||
FailedEntityMigrationBuildResult<FailedFlatViewValidation>;
|
||||
|
||||
export type WorkspaceViewMigrationBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceViewMigrationV2BuilderOptions;
|
||||
dependencyOptimisticEntityMaps: WorkspaceMigrationOrchestratorOptimisticEntityMaps;
|
||||
} & FromTo<FlatViewMaps, 'FlatViewMaps'>;
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceViewMigrationBuilderV2Service extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
FlatViewMaps,
|
||||
FlatView,
|
||||
FailedFlatViewValidation,
|
||||
WorkspaceMigrationViewActionV2,
|
||||
WorkspaceViewMigrationBuildArgs
|
||||
> {
|
||||
constructor(
|
||||
private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async validateAndBuildActions(
|
||||
args: WorkspaceViewMigrationBuildArgs & {
|
||||
created: FlatView[];
|
||||
updated: { from: FlatView; to: FlatView }[];
|
||||
deleted: FlatView[];
|
||||
},
|
||||
): Promise<{
|
||||
failed: FailedFlatViewValidation[];
|
||||
created: WorkspaceMigrationViewActionV2[];
|
||||
deleted: WorkspaceMigrationViewActionV2[];
|
||||
updated: WorkspaceMigrationViewActionV2[];
|
||||
optimisticMaps: FlatViewMaps;
|
||||
}> {
|
||||
const viewActionsValidateAndBuildResult =
|
||||
await this.workspaceMigrationV2ViewActionsBuilderService.validateAndBuildViewActions(
|
||||
{
|
||||
fromFlatViewMaps: args.fromFlatViewMaps,
|
||||
toFlatViewMaps: args.toFlatViewMaps,
|
||||
createdFlatView: args.created,
|
||||
updatedFlatView: args.updated,
|
||||
deletedFlatView: args.deleted,
|
||||
buildOptions: args.buildOptions,
|
||||
dependencyOptimisticEntityMaps: args.dependencyOptimisticEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
failed: viewActionsValidateAndBuildResult.failed,
|
||||
created: viewActionsValidateAndBuildResult.created,
|
||||
deleted: viewActionsValidateAndBuildResult.deleted,
|
||||
updated: viewActionsValidateAndBuildResult.updated,
|
||||
optimisticMaps: viewActionsValidateAndBuildResult.optimisticFlatViewMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { type FailedFlatViewValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation.type';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type ValidateAndBuildViewResult<T extends WorkspaceMigrationActionV2> = {
|
||||
failed: FailedFlatViewValidation[];
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: T[];
|
||||
optimisticFlatViewMaps: FlatViewMaps;
|
||||
};
|
||||
+3
-1
@@ -2,12 +2,14 @@ import { type WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manag
|
||||
import { type WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type WorkspaceMigrationObjectActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { type WorkspaceMigrationViewActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { type WorkspaceMigrationViewFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
|
||||
export type WorkspaceMigrationActionV2 =
|
||||
| WorkspaceMigrationObjectActionV2
|
||||
| WorkspaceMigrationFieldActionV2
|
||||
| WorkspaceMigrationIndexActionV2
|
||||
| WorkspaceMigrationViewActionV2;
|
||||
| WorkspaceMigrationViewActionV2
|
||||
| WorkspaceMigrationViewFieldActionV2;
|
||||
|
||||
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];
|
||||
|
||||
|
||||
+28
-2
@@ -1,11 +1,37 @@
|
||||
import { type FlatView } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-view.type';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type FlatViewPropertiesToCompare } from 'src/engine/core-modules/view/flat-view/types/flat-view-properties-to-compare.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export type FlatViewPropertyUpdate<P extends FlatViewPropertiesToCompare> = {
|
||||
property: P;
|
||||
} & FromTo<ViewEntity[P]>;
|
||||
|
||||
export type CreateViewAction = {
|
||||
type: 'create_view';
|
||||
view: FlatView;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationViewActionV2 = CreateViewAction;
|
||||
export type UpdateViewAction = {
|
||||
type: 'update_view';
|
||||
viewId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatViewPropertiesToCompare]: FlatViewPropertyUpdate<P>;
|
||||
}[FlatViewPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
export type DeleteViewAction = {
|
||||
type: 'delete_view';
|
||||
viewId: string;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationViewActionV2 =
|
||||
| CreateViewAction
|
||||
| UpdateViewAction
|
||||
| DeleteViewAction;
|
||||
|
||||
export type WorkspaceMigrationViewActionTypeV2 =
|
||||
WorkspaceMigrationViewActionV2['type'];
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { type FlatViewFieldPropertiesToCompare } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-properties-to-compare.type';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export type FlatViewFieldPropertyUpdate<
|
||||
P extends FlatViewFieldPropertiesToCompare,
|
||||
> = {
|
||||
property: P;
|
||||
} & FromTo<ViewFieldEntity[P]>;
|
||||
|
||||
export type CreateViewFieldAction = {
|
||||
type: 'create_view_field';
|
||||
viewField: FlatViewField;
|
||||
};
|
||||
|
||||
export type UpdateViewFieldAction = {
|
||||
type: 'update_view_field';
|
||||
viewFieldId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatViewFieldPropertiesToCompare]: FlatViewFieldPropertyUpdate<P>;
|
||||
}[FlatViewFieldPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
export type DeleteViewFieldAction = {
|
||||
type: 'delete_view_field';
|
||||
viewFieldId: string;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationViewFieldActionV2 =
|
||||
| CreateViewFieldAction
|
||||
| UpdateViewFieldAction
|
||||
| DeleteViewFieldAction;
|
||||
|
||||
export type WorkspaceMigrationViewFieldActionTypeV2 =
|
||||
WorkspaceMigrationViewFieldActionV2['type'];
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import {
|
||||
type CreateViewAction,
|
||||
type DeleteViewAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
|
||||
export const getWorkspaceMigrationV2ViewCreateAction = ({
|
||||
flatView,
|
||||
}: {
|
||||
flatView: FlatView;
|
||||
}): CreateViewAction => ({
|
||||
type: 'create_view',
|
||||
view: flatView,
|
||||
});
|
||||
|
||||
export const getWorkspaceMigrationV2ViewDeleteAction = (
|
||||
flatView: FlatView,
|
||||
): DeleteViewAction => ({
|
||||
type: 'delete_view',
|
||||
viewId: flatView.id,
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import {
|
||||
type CreateViewFieldAction,
|
||||
type DeleteViewFieldAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
|
||||
export const getWorkspaceMigrationV2ViewFieldCreateAction = ({
|
||||
flatViewField,
|
||||
}: {
|
||||
flatViewField: FlatViewField;
|
||||
}): CreateViewFieldAction => ({
|
||||
type: 'create_view_field',
|
||||
viewField: flatViewField,
|
||||
});
|
||||
|
||||
export const getWorkspaceMigrationV2ViewFieldDeleteAction = (
|
||||
flatViewField: FlatViewField,
|
||||
): DeleteViewFieldAction => ({
|
||||
type: 'delete_view_field',
|
||||
viewFieldId: flatViewField.id,
|
||||
});
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { FailedFlatViewFieldValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-field-validation.type';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.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';
|
||||
|
||||
@Injectable()
|
||||
export class FlatViewFieldValidatorService {
|
||||
constructor() {}
|
||||
|
||||
public validateFlatViewFieldUpdate({
|
||||
existingFlatViewFieldMaps,
|
||||
updatedFlatViewField,
|
||||
optimisticFlatViewMaps,
|
||||
}: {
|
||||
existingFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
updatedFlatViewField: FlatViewField;
|
||||
optimisticFlatViewMaps: FlatViewMaps;
|
||||
}): FailedFlatViewFieldValidation {
|
||||
const errors = [];
|
||||
|
||||
const optimisticFlatView =
|
||||
optimisticFlatViewMaps.byId[updatedFlatViewField.viewId];
|
||||
|
||||
if (!isDefined(optimisticFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const existingFlatViewField =
|
||||
existingFlatViewFieldMaps.byId[updatedFlatViewField.id];
|
||||
|
||||
if (!isDefined(existingFlatViewField)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field not found`,
|
||||
userFriendlyMessage: t`View field not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'update_view_field',
|
||||
viewFieldLevelErrors: errors,
|
||||
failedViewFieldValidationMinimalInformation: {
|
||||
id: updatedFlatViewField.id,
|
||||
viewId: updatedFlatViewField.viewId,
|
||||
fieldMetadataId: updatedFlatViewField.fieldMetadataId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public validateFlatViewFieldDeletion({
|
||||
existingFlatViewFieldMaps,
|
||||
viewFieldIdToDelete,
|
||||
optimisticFlatViewMaps,
|
||||
}: {
|
||||
existingFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
viewFieldIdToDelete: string;
|
||||
optimisticFlatViewMaps: FlatViewMaps;
|
||||
}): FailedFlatViewFieldValidation {
|
||||
const errors = [];
|
||||
|
||||
const optimisticFlatView = optimisticFlatViewMaps.byId[viewFieldIdToDelete];
|
||||
|
||||
if (!isDefined(optimisticFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const existingFlatViewField =
|
||||
existingFlatViewFieldMaps.byId[viewFieldIdToDelete];
|
||||
|
||||
if (!isDefined(existingFlatViewField)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field not found`,
|
||||
userFriendlyMessage: t`View field not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'delete_view_field',
|
||||
viewFieldLevelErrors: errors,
|
||||
failedViewFieldValidationMinimalInformation: {
|
||||
id: viewFieldIdToDelete,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async validateFlatViewFieldCreation({
|
||||
_existingFlatViewFieldMaps,
|
||||
flatViewFieldToValidate,
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
optimisticFlatViewMaps,
|
||||
}: {
|
||||
_existingFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
optimisticFlatViewMaps: FlatViewMaps;
|
||||
}): Promise<FailedFlatViewFieldValidation> {
|
||||
const errors = [];
|
||||
|
||||
const relatedFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Field metadata not found`,
|
||||
userFriendlyMessage: t`Field metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const optimisticFlatView =
|
||||
optimisticFlatViewMaps.byId[flatViewFieldToValidate.viewId];
|
||||
|
||||
if (!isDefined(optimisticFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'create_view_field',
|
||||
viewFieldLevelErrors: errors,
|
||||
failedViewFieldValidationMinimalInformation: {
|
||||
id: flatViewFieldToValidate.id,
|
||||
viewId: flatViewFieldToValidate.viewId,
|
||||
fieldMetadataId: flatViewFieldToValidate.fieldMetadataId,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { FailedFlatViewValidation } from 'src/engine/core-modules/view/flat-view/types/failed-flat-view-validation.type';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
@Injectable()
|
||||
export class FlatViewValidatorService {
|
||||
constructor() {}
|
||||
|
||||
public validateFlatViewUpdate({
|
||||
existingFlatViewMaps,
|
||||
updatedFlatView,
|
||||
}: {
|
||||
existingFlatViewMaps: FlatViewMaps;
|
||||
updatedFlatView: FlatView;
|
||||
}): FailedFlatViewValidation {
|
||||
const errors = [];
|
||||
|
||||
const existingFlatView = existingFlatViewMaps.byId[updatedFlatView.id];
|
||||
|
||||
if (!isDefined(existingFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'update_view',
|
||||
viewLevelErrors: errors,
|
||||
failedViewValidationMinimalInformation: {
|
||||
id: updatedFlatView.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public validateFlatViewDeletion({
|
||||
existingFlatViewMaps,
|
||||
viewIdToDelete,
|
||||
}: {
|
||||
existingFlatViewMaps: FlatViewMaps;
|
||||
viewIdToDelete: string;
|
||||
}): FailedFlatViewValidation {
|
||||
const errors = [];
|
||||
|
||||
const existingFlatView = existingFlatViewMaps.byId[viewIdToDelete];
|
||||
|
||||
if (!isDefined(existingFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'delete_view',
|
||||
viewLevelErrors: errors,
|
||||
failedViewValidationMinimalInformation: {
|
||||
id: viewIdToDelete,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async validateFlatViewCreation({
|
||||
_existingFlatViewMaps,
|
||||
flatViewToValidate,
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
}: {
|
||||
_existingFlatViewMaps: FlatViewMaps;
|
||||
flatViewToValidate: FlatView;
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
}): Promise<FailedFlatViewValidation> {
|
||||
const optimisticFlatObjectMetadata =
|
||||
optimisticFlatObjectMetadataMaps.byId[
|
||||
flatViewToValidate.objectMetadataId
|
||||
];
|
||||
|
||||
const errors = [];
|
||||
|
||||
if (!isDefined(optimisticFlatObjectMetadata)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Object metadata not found`,
|
||||
userFriendlyMessage: t`Object metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'create_view',
|
||||
viewLevelErrors: errors,
|
||||
failedViewValidationMinimalInformation: {
|
||||
id: flatViewToValidate.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
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],
|
||||
})
|
||||
export class WorkspaceMigrationBuilderValidatorsModule {}
|
||||
+15
-2
@@ -6,10 +6,15 @@ import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/f
|
||||
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 { 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';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceViewFieldMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-field-migration-builder-v2.service';
|
||||
import { WorkspaceViewMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-view-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module';
|
||||
|
||||
@Module({
|
||||
imports: [FeatureFlagModule],
|
||||
imports: [FeatureFlagModule, WorkspaceMigrationBuilderValidatorsModule],
|
||||
providers: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
FlatFieldMetadataValidatorService,
|
||||
@@ -17,7 +22,15 @@ import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager
|
||||
WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceViewMigrationBuilderV2Service,
|
||||
WorkspaceViewFieldMigrationBuilderV2Service,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
WorkspaceViewMigrationBuilderV2Service,
|
||||
WorkspaceViewFieldMigrationBuilderV2Service,
|
||||
],
|
||||
exports: [WorkspaceMigrationBuilderV2Service],
|
||||
})
|
||||
export class WorkspaceMigrationBuilderV2Module {}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { CreateViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'create_view_field',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { viewField } = action;
|
||||
|
||||
const viewFieldRepository =
|
||||
queryRunner.manager.getRepository<ViewFieldEntity>(ViewFieldEntity);
|
||||
|
||||
await viewFieldRepository.save({
|
||||
...viewField,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<CreateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { DeleteViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewFieldActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'delete_view_field',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteViewFieldAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { viewFieldId } = action;
|
||||
|
||||
const viewFieldRepository =
|
||||
queryRunner.manager.getRepository<ViewFieldEntity>(ViewFieldEntity);
|
||||
|
||||
await viewFieldRepository.delete({
|
||||
id: viewFieldId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteViewFieldAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { UpdateViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
import { fromWorkspaceMigrationUpdateActionToPartialEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-workspace-migration-update-action-to-partial-field-or-object-entity.util';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewFieldActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'update_view_field',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner } = context;
|
||||
const { viewFieldId } = action;
|
||||
|
||||
const viewFieldRepository =
|
||||
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
|
||||
|
||||
await viewFieldRepository.update(
|
||||
viewFieldId,
|
||||
fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
);
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<UpdateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+1
-14
@@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { CreateViewAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
@@ -21,25 +20,13 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { view } = action;
|
||||
|
||||
const { viewFields } = view;
|
||||
|
||||
const viewRepository =
|
||||
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
|
||||
const viewFieldRepository =
|
||||
queryRunner.manager.getRepository<ViewFieldEntity>(ViewFieldEntity);
|
||||
|
||||
const createdView = await viewRepository.save({
|
||||
await viewRepository.save({
|
||||
...view,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await viewFieldRepository.save(
|
||||
viewFields.map((viewField) => ({
|
||||
...viewField,
|
||||
viewId: createdView.id,
|
||||
workspaceId,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { DeleteViewAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'delete_view',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteViewAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { viewId } = action;
|
||||
|
||||
const viewRepository =
|
||||
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
|
||||
|
||||
await viewRepository.delete({
|
||||
id: viewId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteViewAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { UpdateViewAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
import { fromWorkspaceMigrationUpdateActionToPartialEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-workspace-migration-update-action-to-partial-field-or-object-entity.util';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'update_view',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateViewAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner } = context;
|
||||
const { viewId } = action;
|
||||
|
||||
const viewRepository =
|
||||
queryRunner.manager.getRepository<ViewEntity>(ViewEntity);
|
||||
|
||||
await viewRepository.update(
|
||||
viewId,
|
||||
fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
);
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<UpdateViewAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+11
@@ -9,7 +9,12 @@ import { DeleteIndexActionHandlerService } from 'src/engine/workspace-manager/wo
|
||||
import { CreateObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/object/services/create-object-action-handler.service';
|
||||
import { DeleteObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/object/services/delete-object-action-handler.service';
|
||||
import { UpdateObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/object/services/update-object-action-handler.service';
|
||||
import { CreateViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/create-view-field-action-handler.service';
|
||||
import { DeleteViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/delete-view-field-action-handler.service';
|
||||
import { UpdateViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/update-view-field-action-handler.service';
|
||||
import { CreateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/create-view-action-handler.service';
|
||||
import { DeleteViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/delete-view-action-handler.service';
|
||||
import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/update-view-action-handler.service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceSchemaManagerModule],
|
||||
@@ -26,6 +31,12 @@ import { CreateViewActionHandlerService } from 'src/engine/workspace-manager/wor
|
||||
DeleteIndexActionHandlerService,
|
||||
|
||||
CreateViewActionHandlerService,
|
||||
UpdateViewActionHandlerService,
|
||||
DeleteViewActionHandlerService,
|
||||
|
||||
CreateViewFieldActionHandlerService,
|
||||
UpdateViewFieldActionHandlerService,
|
||||
DeleteViewFieldActionHandlerService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceSchemaMigrationRunnerActionHandlersModule {}
|
||||
|
||||
+5
@@ -135,6 +135,11 @@ export const applyWorkspaceMigrationActionOnFlatObjectMetadataMaps = <
|
||||
case 'create_index':
|
||||
case 'delete_index':
|
||||
case 'create_view':
|
||||
case 'update_view':
|
||||
case 'delete_view':
|
||||
case 'create_view_field':
|
||||
case 'update_view_field':
|
||||
case 'delete_view_field':
|
||||
return flatObjectMetadataMaps;
|
||||
default: {
|
||||
assertUnreachable(
|
||||
|
||||
+7
-1
@@ -1,8 +1,14 @@
|
||||
import { type UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type UpdateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { type UpdateViewAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { type UpdateViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
|
||||
export const fromWorkspaceMigrationUpdateActionToPartialEntity = <
|
||||
T extends UpdateFieldAction | UpdateObjectAction,
|
||||
T extends
|
||||
| UpdateFieldAction
|
||||
| UpdateObjectAction
|
||||
| UpdateViewAction
|
||||
| UpdateViewFieldAction,
|
||||
>(
|
||||
action: T,
|
||||
) => {
|
||||
|
||||
+13
-2
@@ -1,5 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ViewCacheModule } from 'src/engine/core-modules/view/cache/services/view-cache.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMigrationBuildOrchestratorService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
import { WorkspaceMigrationBuilderV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module';
|
||||
import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.module';
|
||||
@@ -8,8 +11,16 @@ import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/w
|
||||
imports: [
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
WorkspaceMetadataCacheModule,
|
||||
ViewCacheModule,
|
||||
],
|
||||
providers: [
|
||||
WorkspaceMigrationValidateBuildAndRunService,
|
||||
WorkspaceMigrationBuildOrchestratorService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceMigrationValidateBuildAndRunService,
|
||||
WorkspaceMigrationBuildOrchestratorService,
|
||||
],
|
||||
providers: [WorkspaceMigrationValidateBuildAndRunService],
|
||||
exports: [WorkspaceMigrationValidateBuildAndRunService],
|
||||
})
|
||||
export class WorkspaceMigrationV2Module {}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ import { Column, Index } from 'typeorm';
|
||||
})
|
||||
export abstract class SyncableEntity {
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
universalIdentifier?: string | null;
|
||||
universalIdentifier: string | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user