Flat entity maps cache generic service + runner dynamically retrieving invalidating update cache + view service v2 refactor (#14508)
# Introduction Migrate previous runner only iterating on `flatObjectMetadataMaps` to `allFlatEntityMaps`. Refactored the optimistic to be handled inside the actions handler ## Workspace flat map cache Introducing a new service and registry, that will dynamically retrieve and or recompute requested cache when called ## Runner refactor Runner now dynamically invalidate updated cache at the end of the transaction close https://github.com/orgs/twentyhq/projects/1/views/8?pane=issue&itemId=129136356&issue=twentyhq%7Ccore-team-issues%7C1492 close https://github.com/orgs/twentyhq/projects/1/views/8?pane=issue&itemId=129136210&issue=twentyhq%7Ccore-team-issues%7C1494
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
|
||||
export const ALL_FLAT_ENTITY_MAPS_PROPERTIES = [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewMaps',
|
||||
] as const satisfies (keyof AllFlatEntityMaps)[];
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all
|
||||
|
||||
export const EMPTY_ALL_FLAT_ENTITY_MAPS = {
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {},
|
||||
...EMPTY_FLAT_ENTITY_MAPS,
|
||||
idByNameSingular: {},
|
||||
},
|
||||
flatViewFieldMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
import { WorkspaceFlatMapCacheModule } from 'src/engine/workspace-flat-map-cache/workspace-flat-map-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceFlatMapCacheModule],
|
||||
providers: [WorkspaceManyOrAllFlatEntityMapsCacheService],
|
||||
exports: [WorkspaceManyOrAllFlatEntityMapsCacheService],
|
||||
})
|
||||
export class WorkspaceManyOrAllFlatEntityMapsCacheModule {}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_FLAT_ENTITY_MAPS_PROPERTIES } from 'src/engine/core-modules/common/constant/all-flat-entity-maps-properties.constant';
|
||||
import { EMPTY_ALL_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-all-flat-entity-maps.constant';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import {
|
||||
WorkspaceFlatMapCacheException,
|
||||
WorkspaceFlatMapCacheExceptionCode,
|
||||
} from 'src/engine/workspace-flat-map-cache/exceptions/workspace-flat-map-cache.exception';
|
||||
import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceManyOrAllFlatEntityMapsCacheService {
|
||||
private readonly logger = new Logger(
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheService.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly cacheRegistry: WorkspaceFlatMapCacheRegistryService,
|
||||
) {}
|
||||
|
||||
public async getOrRecomputeManyOrAllFlatEntityMaps<
|
||||
T extends (keyof AllFlatEntityMaps)[] = (keyof AllFlatEntityMaps)[],
|
||||
>({
|
||||
flatEntities,
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
flatEntities?: T;
|
||||
}): Promise<Pick<AllFlatEntityMaps, T[number]>> {
|
||||
const allFlatEntityMaps: AllFlatEntityMaps = structuredClone(
|
||||
EMPTY_ALL_FLAT_ENTITY_MAPS,
|
||||
);
|
||||
|
||||
for (const flatEntityName of ALL_FLAT_ENTITY_MAPS_PROPERTIES) {
|
||||
if (isDefined(flatEntities) && !flatEntities.includes(flatEntityName)) {
|
||||
delete allFlatEntityMaps[flatEntityName];
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const service = this.cacheRegistry.getCacheService(flatEntityName);
|
||||
|
||||
if (!isDefined(service)) {
|
||||
throw new WorkspaceFlatMapCacheException(
|
||||
`No cache service found for ${flatEntityName}`,
|
||||
WorkspaceFlatMapCacheExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await service.getExistingOrRecomputeFlatMaps({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
// @ts-expect-error todo prastoin once refactored flat object metadata cache
|
||||
allFlatEntityMaps[flatEntityName] = result;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to get flat entity maps for ${flatEntityName}`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return allFlatEntityMaps;
|
||||
}
|
||||
|
||||
public async invalidateFlatEntityMaps<
|
||||
T extends (keyof AllFlatEntityMaps)[] = (keyof AllFlatEntityMaps)[],
|
||||
>({
|
||||
flatEntities,
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
flatEntities?: T;
|
||||
}): Promise<void> {
|
||||
for (const flatEntityName of ALL_FLAT_ENTITY_MAPS_PROPERTIES) {
|
||||
if (isDefined(flatEntities) && !flatEntities.includes(flatEntityName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const service = this.cacheRegistry.getCacheService(flatEntityName);
|
||||
|
||||
if (!isDefined(service)) {
|
||||
throw new WorkspaceFlatMapCacheException(
|
||||
`No cache service found for ${flatEntityName}`,
|
||||
WorkspaceFlatMapCacheExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
await service.invalidateCache({ workspaceId });
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to invalidate flat entity maps for ${flatEntityName}`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type AllFlatEntitiesByMetadataEngineName = {
|
||||
// flatFieldMetadata: FlatFieldMetadata;
|
||||
objectMetadata: FlatObjectMetadata;
|
||||
view: FlatView;
|
||||
viewField: FlatViewField;
|
||||
};
|
||||
+3
-9
@@ -1,10 +1,4 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities-by-metadata-engine-name.type';
|
||||
|
||||
export type AllFlatEntitiesByMetadataEngineName = {
|
||||
// flatFieldMetadata: FlatFieldMetadata;
|
||||
objectMetadata: FlatObjectMetadata;
|
||||
view: FlatView;
|
||||
viewField: FlatViewField;
|
||||
};
|
||||
export type AllFlatEntities =
|
||||
AllFlatEntitiesByMetadataEngineName[keyof AllFlatEntitiesByMetadataEngineName];
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ViewCacheService } from 'src/engine/core-modules/view/cache/services/view-cache.service';
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([ViewEntity, ViewFieldEntity])],
|
||||
providers: [ViewCacheService],
|
||||
exports: [ViewCacheService],
|
||||
})
|
||||
export class ViewCacheModule {}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { fromViewFieldEntityToFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/from-view-field-entity-to-flat-view-field.util';
|
||||
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>,
|
||||
@InjectRepository(ViewFieldEntity)
|
||||
private readonly viewFieldRepository: Repository<ViewFieldEntity>,
|
||||
) {}
|
||||
|
||||
async getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<GetExistingOrRecomputeFlatViewMapsResult> {
|
||||
// TODO: get from cache later
|
||||
const existingViews = await this.viewRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: ['viewFields'],
|
||||
select: {
|
||||
viewFields: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const existingFlatViewMaps = generateFlatViewMaps(existingViews);
|
||||
|
||||
return {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
};
|
||||
}
|
||||
|
||||
public async getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<{ flatViewFieldMaps: FlatViewFieldMaps }> {
|
||||
// TODO: get from cache later
|
||||
const existingViewFields = await this.viewFieldRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,93 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewCalendarLayout } from 'src/engine/core-modules/view/enums/view-calendar-layout.enum';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
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 { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
|
||||
|
||||
@InputType()
|
||||
export class CreateViewInput {
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsValidMetadataName()
|
||||
@Field({ nullable: false })
|
||||
name: string;
|
||||
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
objectMetadataId: string;
|
||||
|
||||
@IsEnum(ViewType)
|
||||
@Field(() => ViewType, { nullable: true, defaultValue: ViewType.TABLE })
|
||||
type?: ViewType;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewKey)
|
||||
@Field(() => ViewKey, { nullable: true })
|
||||
key?: ViewKey;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: false })
|
||||
icon: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Field({ nullable: true, defaultValue: 0 })
|
||||
position?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true, defaultValue: false })
|
||||
isCompact?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewOpenRecordIn)
|
||||
@Field(() => ViewOpenRecordIn, {
|
||||
nullable: true,
|
||||
defaultValue: ViewOpenRecordIn.SIDE_PANEL,
|
||||
})
|
||||
openRecordIn?: ViewOpenRecordIn;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(AggregateOperations)
|
||||
@Field(() => AggregateOperations, { nullable: true })
|
||||
kanbanAggregateOperation?: AggregateOperations;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
kanbanAggregateOperationFieldMetadataId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
anyFieldFilterValue?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewCalendarLayout)
|
||||
@Field(() => ViewCalendarLayout, { nullable: true })
|
||||
calendarLayout?: ViewCalendarLayout;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
calendarFieldMetadataId?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
import { IsUUID } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@InputType()
|
||||
export class DeleteViewInput {
|
||||
@IDField(() => UUIDScalarType, {
|
||||
description: 'The id of the view to delete.',
|
||||
})
|
||||
@IsUUID()
|
||||
id: string;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
import { IsUUID } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@InputType()
|
||||
export class DestroyViewInput {
|
||||
@IDField(() => UUIDScalarType, {
|
||||
description: 'The id of the view to destroy.',
|
||||
})
|
||||
@IsUUID()
|
||||
id: string;
|
||||
}
|
||||
@@ -1,50 +1,84 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewCalendarLayout } from 'src/engine/core-modules/view/enums/view-calendar-layout.enum';
|
||||
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 { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
|
||||
|
||||
// TODO: this should be refactored like for view-field.input.ts
|
||||
// This is a temporary fix as we were extending the CreateViewInput class which was adding default values for the non filled fields
|
||||
@InputType()
|
||||
export class UpdateViewInput {
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
@IsValidMetadataName()
|
||||
@Field({ nullable: true })
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewType)
|
||||
@Field(() => ViewType, { nullable: true })
|
||||
type?: ViewType;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true })
|
||||
position?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true })
|
||||
isCompact?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewOpenRecordIn)
|
||||
@Field(() => ViewOpenRecordIn, {
|
||||
nullable: true,
|
||||
})
|
||||
openRecordIn?: ViewOpenRecordIn;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(AggregateOperations)
|
||||
@Field(() => AggregateOperations, { nullable: true })
|
||||
kanbanAggregateOperation?: AggregateOperations;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
kanbanAggregateOperationFieldMetadataId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
anyFieldFilterValue?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewCalendarLayout)
|
||||
@Field(() => ViewCalendarLayout, { nullable: true })
|
||||
calendarLayout?: ViewCalendarLayout;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
calendarFieldMetadataId?: string;
|
||||
}
|
||||
|
||||
@@ -82,17 +82,17 @@ export class ViewDTO {
|
||||
deletedAt?: Date | null;
|
||||
|
||||
@Field(() => [ViewFieldDTO])
|
||||
viewFields: ViewFieldDTO[];
|
||||
viewFields?: ViewFieldDTO[];
|
||||
|
||||
@Field(() => [ViewFilterDTO])
|
||||
viewFilters: ViewFilterDTO[];
|
||||
viewFilters?: ViewFilterDTO[];
|
||||
|
||||
@Field(() => [ViewFilterGroupDTO])
|
||||
viewFilterGroups: ViewFilterGroupDTO[];
|
||||
viewFilterGroups?: ViewFilterGroupDTO[];
|
||||
|
||||
@Field(() => [ViewSortDTO])
|
||||
viewSorts: ViewSortDTO[];
|
||||
viewSorts?: ViewSortDTO[];
|
||||
|
||||
@Field(() => [ViewGroupDTO])
|
||||
viewGroups: ViewGroupDTO[];
|
||||
viewGroups?: ViewGroupDTO[];
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
// We could refactor this type to be dynamic to view type
|
||||
@Entity({ name: 'view', schema: 'core' })
|
||||
@Index('IDX_VIEW_WORKSPACE_ID_OBJECT_METADATA_ID', [
|
||||
'workspaceId',
|
||||
|
||||
+12
-5
@@ -1,8 +1,15 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const FLAT_VIEW_EDITABLE_PROPERTIES = [
|
||||
'isVisible',
|
||||
'size',
|
||||
'name',
|
||||
'type',
|
||||
'icon',
|
||||
'position',
|
||||
'aggregateOperation',
|
||||
] as const satisfies (keyof FlatViewField)[];
|
||||
'isCompact',
|
||||
'openRecordIn',
|
||||
'kanbanAggregateOperation',
|
||||
'kanbanAggregateOperationFieldMetadataId',
|
||||
'anyFieldFilterValue',
|
||||
'calendarLayout',
|
||||
'calendarFieldMetadataId',
|
||||
] as const satisfies (keyof FlatView)[];
|
||||
|
||||
+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_EDITABLE_PROPERTIES = [
|
||||
'isVisible',
|
||||
'size',
|
||||
'position',
|
||||
'aggregateOperation',
|
||||
] as const satisfies (keyof FlatViewField)[];
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-editable-properties.constant';
|
||||
import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-field-editable-properties.constant';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE = [
|
||||
...FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
...FLAT_VIEW_FIELD_EDITABLE_PROPERTIES,
|
||||
'deletedAt',
|
||||
] as const satisfies (keyof FlatViewField)[];
|
||||
|
||||
+2
-9
@@ -1,14 +1,7 @@
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-editable-properties.constant';
|
||||
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',
|
||||
...FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
] as const satisfies (keyof FlatView)[];
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { fromViewFieldEntityToFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/from-view-field-entity-to-flat-view-field.util';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatViewFieldMaps')
|
||||
export class WorkspaceFlatViewFieldMapCacheService extends WorkspaceFlatMapCacheService<FlatViewFieldMaps> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
@InjectRepository(ViewFieldEntity)
|
||||
private readonly viewFieldRepository: Repository<ViewFieldEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatViewFieldMaps> {
|
||||
const existingViewFields = await this.viewFieldRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -13,7 +13,7 @@ import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decor
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('view')
|
||||
@WorkspaceFlatMapCache('flatViewMaps')
|
||||
export class WorkspaceFlatViewMapCacheService extends WorkspaceFlatMapCacheService<FlatViewMaps> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
@@ -33,6 +33,7 @@ export class WorkspaceFlatViewMapCacheService extends WorkspaceFlatMapCacheServi
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: ['viewFields'],
|
||||
select: {
|
||||
viewFields: {
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type CreateViewInput } from 'src/engine/core-modules/view/dtos/inputs/create-view.input';
|
||||
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 fromCreateViewInputToFlatViewToCreate = ({
|
||||
createViewInput: rawCreateViewInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
createViewInput: CreateViewInput;
|
||||
workspaceId: string;
|
||||
}): FlatView => {
|
||||
const { objectMetadataId, ...createViewInput } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawCreateViewInput,
|
||||
['id', 'name', 'objectMetadataId'],
|
||||
);
|
||||
|
||||
const createdAt = new Date();
|
||||
|
||||
return {
|
||||
id: createViewInput.id ?? v4(),
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
name: createViewInput.name,
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
isCustom: true,
|
||||
anyFieldFilterValue: createViewInput.anyFieldFilterValue ?? null,
|
||||
calendarFieldMetadataId: createViewInput.calendarFieldMetadataId ?? null,
|
||||
calendarLayout: createViewInput.calendarLayout ?? null,
|
||||
icon: createViewInput.icon,
|
||||
isCompact: createViewInput.isCompact ?? false,
|
||||
kanbanAggregateOperation: createViewInput.kanbanAggregateOperation ?? null,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
createViewInput.kanbanAggregateOperationFieldMetadataId ?? null,
|
||||
key: createViewInput.key ?? null,
|
||||
openRecordIn: createViewInput.openRecordIn ?? ViewOpenRecordIn.SIDE_PANEL,
|
||||
position: createViewInput.position ?? 0,
|
||||
type: createViewInput.type ?? ViewType.TABLE,
|
||||
universalIdentifier: v4(),
|
||||
viewFieldIds: [],
|
||||
};
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type DeleteViewInput } from 'src/engine/core-modules/view/dtos/inputs/delete-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
} from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
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 fromDeleteViewInputToFlatViewOrThrow = ({
|
||||
deleteViewInput: rawDeleteViewInput,
|
||||
flatViewMaps,
|
||||
}: {
|
||||
deleteViewInput: DeleteViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
}): FlatView => {
|
||||
const { id: viewId } = extractAndSanitizeObjectStringFields(
|
||||
rawDeleteViewInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewToDelete = flatViewMaps.byId[viewId];
|
||||
|
||||
if (!isDefined(existingFlatViewToDelete)) {
|
||||
throw new ViewException(
|
||||
t`View to delete not found`,
|
||||
ViewExceptionCode.VIEW_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...existingFlatViewToDelete,
|
||||
deletedAt: new Date(),
|
||||
};
|
||||
};
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type DestroyViewInput } from 'src/engine/core-modules/view/dtos/inputs/destroy-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
} from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
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 fromDestroyViewInputToFlatViewOrThrow = ({
|
||||
destroyViewInput: rawDestroyViewInput,
|
||||
flatViewMaps,
|
||||
}: {
|
||||
destroyViewInput: DestroyViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
}): FlatView => {
|
||||
const { id: viewId } = extractAndSanitizeObjectStringFields(
|
||||
rawDestroyViewInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewToDestroy = flatViewMaps.byId[viewId];
|
||||
|
||||
if (!isDefined(existingFlatViewToDestroy)) {
|
||||
throw new ViewException(
|
||||
t`View to destroy not found`,
|
||||
ViewExceptionCode.VIEW_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return existingFlatViewToDestroy;
|
||||
};
|
||||
+3
-3
@@ -10,7 +10,7 @@ import {
|
||||
ViewFieldException,
|
||||
ViewFieldExceptionCode,
|
||||
} from 'src/engine/core-modules/view/exceptions/view-field.exception';
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-editable-properties.constant';
|
||||
import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-field-editable-properties.constant';
|
||||
import { type FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
@@ -39,12 +39,12 @@ export const fromUpdateViewFieldInputToFlatViewFieldToUpdateOrThrow = ({
|
||||
}
|
||||
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateViewFieldInput.update,
|
||||
FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
FLAT_VIEW_FIELD_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
return mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewFieldToUpdate,
|
||||
properties: FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
properties: FLAT_VIEW_FIELD_EDITABLE_PROPERTIES,
|
||||
update: updatedEditableFieldProperties,
|
||||
});
|
||||
};
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type UpdateViewInput } from 'src/engine/core-modules/view/dtos/inputs/update-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
} from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/core-modules/view/flat-view/constants/flat-view-editable-properties.constant';
|
||||
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';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
|
||||
export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
updateViewInput: rawUpdateViewInput,
|
||||
flatViewMaps,
|
||||
}: {
|
||||
updateViewInput: UpdateViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
}): FlatView => {
|
||||
const { id: viewToUpdateId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateViewInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewToUpdate = flatViewMaps.byId[viewToUpdateId];
|
||||
|
||||
if (!isDefined(existingFlatViewToUpdate)) {
|
||||
throw new ViewException(
|
||||
t`View to update not found`,
|
||||
ViewExceptionCode.VIEW_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateViewInput,
|
||||
FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
return mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewToUpdate,
|
||||
properties: FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
update: updatedEditableFieldProperties,
|
||||
});
|
||||
};
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
import { isArray } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
||||
import { CreateViewInput } from 'src/engine/core-modules/view/dtos/inputs/create-view.input';
|
||||
import { UpdateViewInput } from 'src/engine/core-modules/view/dtos/inputs/update-view.input';
|
||||
@@ -26,6 +28,7 @@ import { ViewFilterGroupService } from 'src/engine/core-modules/view/services/vi
|
||||
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 { ViewGraphqlApiExceptionFilter } from 'src/engine/core-modules/view/utils/view-graphql-api-exception.filter';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@@ -45,6 +48,8 @@ export class ViewResolver {
|
||||
private readonly viewFilterGroupService: ViewFilterGroupService,
|
||||
private readonly viewSortService: ViewSortService,
|
||||
private readonly viewGroupService: ViewGroupService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly viewV2Service: ViewV2Service,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => String)
|
||||
@@ -119,6 +124,19 @@ export class ViewResolver {
|
||||
@Args('input') input: CreateViewInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<ViewDTO> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.viewV2Service.createOne({
|
||||
createViewInput: input,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
return this.viewService.create({
|
||||
...input,
|
||||
workspaceId: workspace.id,
|
||||
@@ -131,6 +149,19 @@ export class ViewResolver {
|
||||
@Args('input') input: UpdateViewInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<ViewDTO> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.viewV2Service.updateOne({
|
||||
updateViewInput: input,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
return this.viewService.update(id, workspace.id, input);
|
||||
}
|
||||
|
||||
@@ -139,6 +170,21 @@ export class ViewResolver {
|
||||
@Args('id', { type: () => String }) id: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<boolean> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
const deletedView = await this.viewV2Service.deleteOne({
|
||||
deleteViewInput: { id },
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
return isDefined(deletedView);
|
||||
}
|
||||
|
||||
const deletedView = await this.viewService.delete(id, workspace.id);
|
||||
|
||||
return isDefined(deletedView);
|
||||
@@ -149,6 +195,21 @@ export class ViewResolver {
|
||||
@Args('id', { type: () => String }) id: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<boolean> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
const deletedView = await this.viewV2Service.destroyOne({
|
||||
destroyViewInput: { id },
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
return isDefined(deletedView);
|
||||
}
|
||||
|
||||
const deletedView = await this.viewService.destroy(id, workspace.id);
|
||||
|
||||
return isDefined(deletedView);
|
||||
|
||||
+56
-41
@@ -2,12 +2,12 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
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 { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { getSubFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/get-sub-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 { CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
import { DeleteViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/delete-view-field.input';
|
||||
import { DestroyViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/destroy-view-field.input';
|
||||
@@ -17,7 +17,6 @@ import { fromCreateViewFieldInputToFlatViewFieldToCreate } from 'src/engine/core
|
||||
import { fromDeleteViewFieldInputToFlatViewFieldOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-delete-view-field-input-to-flat-view-field-or-throw.util';
|
||||
import { fromDestroyViewFieldInputToFlatViewFieldOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-destroy-view-field-input-to-flat-view-field-or-throw.util';
|
||||
import { fromUpdateViewFieldInputToFlatViewFieldToUpdateOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-update-view-field-input-to-flat-view-field-to-update-or-throw.util';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@@ -25,8 +24,7 @@ import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspa
|
||||
export class ViewFieldV2Service {
|
||||
constructor(
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly viewCacheService: ViewCacheService,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
async createOne({
|
||||
@@ -36,20 +34,20 @@ export class ViewFieldV2Service {
|
||||
createViewFieldInput: CreateViewFieldInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
});
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
});
|
||||
flatEntities: [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const flatViewFieldToCreate =
|
||||
fromCreateViewFieldInputToFlatViewFieldToCreate({
|
||||
@@ -72,7 +70,7 @@ export class ViewFieldV2Service {
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
@@ -91,9 +89,12 @@ export class ViewFieldV2Service {
|
||||
}
|
||||
|
||||
const { flatViewFieldMaps: recomputedExistingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFieldToCreate.id,
|
||||
@@ -109,9 +110,12 @@ export class ViewFieldV2Service {
|
||||
updateViewFieldInput: UpdateViewFieldInput;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatView =
|
||||
fromUpdateViewFieldInputToFlatViewFieldToUpdateOrThrow({
|
||||
@@ -153,9 +157,12 @@ export class ViewFieldV2Service {
|
||||
}
|
||||
|
||||
const { flatViewFieldMaps: recomputedExistingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: optimisticallyUpdatedFlatView.id,
|
||||
@@ -171,9 +178,12 @@ export class ViewFieldV2Service {
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatViewWithDeletedAt =
|
||||
fromDeleteViewFieldInputToFlatViewFieldOrThrow({
|
||||
@@ -211,9 +221,12 @@ export class ViewFieldV2Service {
|
||||
}
|
||||
|
||||
const { flatViewFieldMaps: recomputedExistingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: optimisticallyUpdatedFlatViewWithDeletedAt.id,
|
||||
@@ -228,14 +241,16 @@ export class ViewFieldV2Service {
|
||||
destroyViewFieldInput: DestroyViewFieldInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.viewCacheService.getExistingFlatViewFieldMapsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
});
|
||||
const {
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
} =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewFieldMaps', 'flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const existingViewFieldToDelete =
|
||||
fromDestroyViewFieldInputToFlatViewFieldOrThrow({
|
||||
|
||||
@@ -1,75 +1,53 @@
|
||||
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 { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
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 { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { getSubFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/get-sub-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 { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { CreateViewInput } from 'src/engine/core-modules/view/dtos/inputs/create-view.input';
|
||||
import { DeleteViewInput } from 'src/engine/core-modules/view/dtos/inputs/delete-view.input';
|
||||
import { DestroyViewInput } from 'src/engine/core-modules/view/dtos/inputs/destroy-view.input';
|
||||
import { UpdateViewInput } from 'src/engine/core-modules/view/dtos/inputs/update-view.input';
|
||||
import { ViewDTO } from 'src/engine/core-modules/view/dtos/view.dto';
|
||||
import { fromCreateViewInputToFlatViewToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-input-to-flat-view-to-create.util';
|
||||
import { fromDeleteViewInputToFlatViewOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-delete-view-input-to-flat-view-or-throw.util';
|
||||
import { fromDestroyViewInputToFlatViewOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-destroy-view-input-to-flat-view-or-throw.util';
|
||||
import { fromUpdateViewInputToFlatViewToUpdateOrThrow } from 'src/engine/core-modules/view/flat-view/utils/from-update-view-input-to-flat-view-to-update-or-throw.util';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class ViewV2Service {
|
||||
constructor(
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly viewCacheService: ViewCacheService,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
async createOne(viewData: Partial<ViewEntity>): Promise<ViewEntity> {
|
||||
const { workspaceId } = viewData;
|
||||
|
||||
if (!isDefined(workspaceId)) {
|
||||
throw new ViewException(
|
||||
generateViewExceptionMessage(
|
||||
ViewExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
{
|
||||
userFriendlyMessage: generateViewUserFriendlyExceptionMessage(
|
||||
ViewExceptionMessageKey.WORKSPACE_ID_REQUIRED,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
async createOne({
|
||||
createViewInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
createViewInput: CreateViewInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewDTO> {
|
||||
const { flatObjectMetadataMaps, flatViewMaps: existingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps', 'flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.viewCacheService.getExistingOrRecomputeFlatViewMaps({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const flatViewFromCreateInput = fromPartialFlatViewToFlatViewWithDefault({
|
||||
...viewData,
|
||||
universalIdentifier: viewData.universalIdentifier ?? v4(),
|
||||
const flatViewFromCreateInput = fromCreateViewInputToFlatViewToCreate({
|
||||
createViewInput,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const toFlatViewMaps: FlatViewMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
const toFlatViewMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFromCreateInput,
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
@@ -84,7 +62,7 @@ export class ViewV2Service {
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataMaps: flatObjectMetadataMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
@@ -101,49 +79,40 @@ export class ViewV2Service {
|
||||
);
|
||||
}
|
||||
|
||||
const [createdView] = await this.viewRepository.find({
|
||||
where: {
|
||||
id: flatViewFromCreateInput.id,
|
||||
},
|
||||
});
|
||||
const { flatViewMaps: recomputedExistingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return createdView;
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFromCreateInput.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async updateOne(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
updateData: Partial<ViewEntity>,
|
||||
): Promise<ViewEntity> {
|
||||
async updateOne({
|
||||
updateViewInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
updateViewInput: UpdateViewInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewDTO> {
|
||||
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,
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const existingViewToUpdate = removePropertiesFromRecord(
|
||||
{
|
||||
...existingView,
|
||||
...updateData,
|
||||
},
|
||||
VIEW_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
const flatViewFromUpdateInput = fromPartialFlatViewToFlatViewWithDefault({
|
||||
...existingViewToUpdate,
|
||||
universalIdentifier: existingViewToUpdate.universalIdentifier ?? '',
|
||||
});
|
||||
const flatViewFromUpdateInput =
|
||||
fromUpdateViewInputToFlatViewToUpdateOrThrow({
|
||||
updateViewInput,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const fromFlatViewMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: [flatViewFromUpdateInput.id],
|
||||
@@ -178,42 +147,48 @@ export class ViewV2Service {
|
||||
);
|
||||
}
|
||||
|
||||
const [updatedView] = await this.viewRepository.find({
|
||||
where: {
|
||||
id: Equal(id),
|
||||
},
|
||||
});
|
||||
const { flatViewMaps: recomputedExistingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return updatedView;
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: updateViewInput.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOne(id: string, workspaceId: string): Promise<boolean> {
|
||||
async deleteOne({
|
||||
deleteViewInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
deleteViewInput: DeleteViewInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewDTO> {
|
||||
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,
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const flatViewFromDeleteInput = fromDeleteViewInputToFlatViewOrThrow({
|
||||
deleteViewInput,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const fromFlatViewMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: [existingViewToDelete.id],
|
||||
flatEntityIds: [flatViewFromDeleteInput.id],
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
const toFlatViewMaps: FlatViewMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: fromFlatViewMaps,
|
||||
entityToDeleteId: existingViewToDelete.id,
|
||||
});
|
||||
const toFlatViewMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFromDeleteInput,
|
||||
flatEntityMaps: fromFlatViewMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
@@ -239,6 +214,84 @@ export class ViewV2Service {
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
const { flatViewMaps: recomputedExistingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: deleteViewInput.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async destroyOne({
|
||||
destroyViewInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
destroyViewInput: DestroyViewInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewDTO> {
|
||||
const { flatViewMaps: existingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatViewFromDestroyInput = fromDestroyViewInputToFlatViewOrThrow({
|
||||
destroyViewInput,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const fromFlatViewMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: [flatViewFromDestroyInput.id],
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
const toFlatViewMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: fromFlatViewMaps,
|
||||
entityToDeleteId: flatViewFromDestroyInput.id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewMaps: {
|
||||
from: fromFlatViewMaps,
|
||||
to: toFlatViewMaps,
|
||||
},
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while destroying view',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatViewMaps: recomputedExistingFlatViewMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatViewMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: destroyViewInput.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
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';
|
||||
@@ -50,8 +50,8 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
ViewCacheModule,
|
||||
FlatViewModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
controllers: [
|
||||
ViewController,
|
||||
|
||||
+2
@@ -9,6 +9,7 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { ActorModule } from 'src/engine/core-modules/actor/actor.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { CoreViewModule } from 'src/engine/core-modules/view/view.module';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
@@ -66,6 +67,7 @@ import { FieldMetadataService } from './services/field-metadata.service';
|
||||
WorkspaceMigrationV2Module,
|
||||
FlatFieldMetadataModule,
|
||||
IndexMetadataModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
services: [
|
||||
IsFieldMetadataDefaultValue,
|
||||
|
||||
+14
-7
@@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
@@ -22,7 +23,6 @@ import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@@ -31,7 +31,7 @@ export class FieldMetadataServiceV2 {
|
||||
constructor(
|
||||
@InjectRepository(FieldMetadataEntity)
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {}
|
||||
|
||||
@@ -65,9 +65,10 @@ export class FieldMetadataServiceV2 {
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataDTO> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -136,8 +137,11 @@ export class FieldMetadataServiceV2 {
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const inputTranspilationResult = fromUpdateFieldInputToFlatFieldMetadata({
|
||||
@@ -221,8 +225,11 @@ export class FieldMetadataServiceV2 {
|
||||
}
|
||||
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const allTranspiledTranspilationInputs = [];
|
||||
|
||||
+1
@@ -3,4 +3,5 @@ import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-ob
|
||||
export const EMPTY_FLAT_OBJECT_METADATA_MAPS = {
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
idByUniversalIdentifier: {},
|
||||
} as const satisfies FlatObjectMetadataMaps;
|
||||
|
||||
+1
@@ -3,4 +3,5 @@ import { type FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-mo
|
||||
export type FlatObjectMetadataMaps = {
|
||||
byId: Partial<Record<string, FlatObjectMetadataWithFlatFieldMaps>>;
|
||||
idByNameSingular: Partial<Record<string, string>>;
|
||||
idByUniversalIdentifier: Record<string, string>;
|
||||
};
|
||||
|
||||
+3
@@ -27,6 +27,9 @@ export const addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow = ({
|
||||
}
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: {
|
||||
...flatObjectMetadataMaps.idByUniversalIdentifier,
|
||||
},
|
||||
byId: {
|
||||
...flatObjectMetadataMaps.byId,
|
||||
[flatFieldMetadata.objectMetadataId]:
|
||||
|
||||
+4
@@ -24,6 +24,10 @@ export const addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow = ({
|
||||
}
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: {
|
||||
...flatObjectMetadataMaps.idByUniversalIdentifier,
|
||||
[flatObjectMetadata.universalIdentifier]: flatObjectMetadata.id,
|
||||
},
|
||||
byId: {
|
||||
...flatObjectMetadataMaps.byId,
|
||||
[flatObjectMetadata.id]:
|
||||
|
||||
+5
@@ -29,6 +29,11 @@ export const addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThr
|
||||
}
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: {
|
||||
...flatObjectMetadataMaps.idByUniversalIdentifier,
|
||||
[flatObjectMetadataWithFlatFieldMaps.universalIdentifier]:
|
||||
flatObjectMetadataWithFlatFieldMaps.id,
|
||||
},
|
||||
byId: {
|
||||
...flatObjectMetadataMaps.byId,
|
||||
[flatObjectMetadataWithFlatFieldMaps.id]:
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ export const deleteFieldFromFlatObjectMetadataMapsOrThrow = ({
|
||||
}
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: flatObjectMetadataMaps.idByUniversalIdentifier,
|
||||
byId: {
|
||||
...flatObjectMetadataMaps.byId,
|
||||
[objectMetadataId]:
|
||||
|
||||
+7
@@ -25,7 +25,14 @@ export const deleteObjectFromFlatObjectMetadataMapsOrThrow = ({
|
||||
flatObjectMetadataMaps.idByNameSingular,
|
||||
).filter(([_nameSingular, id]) => id !== objectMetadataId);
|
||||
|
||||
const updatedIdByUniversalIdentifierEntries = Object.entries(
|
||||
flatObjectMetadataMaps.idByUniversalIdentifier,
|
||||
).filter(([_universalIdentifier, id]) => id !== objectMetadataId);
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: Object.fromEntries(
|
||||
updatedIdByUniversalIdentifierEntries,
|
||||
),
|
||||
byId: removePropertiesFromRecord(flatObjectMetadataMaps.byId, [
|
||||
objectMetadataId,
|
||||
]),
|
||||
|
||||
+9
@@ -24,6 +24,15 @@ export const fromObjectMetadataMapsToFlatObjectMetadataMaps = (
|
||||
}, initialAccumulator);
|
||||
|
||||
return {
|
||||
idByUniversalIdentifier: Object.values(objectMetadataMaps.byId)
|
||||
.filter(isDefined)
|
||||
.reduce(
|
||||
(acc, objectMetadata) => ({
|
||||
...acc,
|
||||
[objectMetadata.standardId ?? objectMetadata.id]: objectMetadata.id,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
idByNameSingular: objectMetadataMaps.idByNameSingular,
|
||||
byId,
|
||||
};
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatObjectMetadataMaps')
|
||||
export class WorkspaceFlatObjectMetadataMapCacheService extends WorkspaceFlatMapCacheService<FlatObjectMetadataMaps> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
private workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
override async beforeInvalidateCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
// Temporarily invalidating old cache too
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
///
|
||||
}
|
||||
|
||||
override async afterInvalidateCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatObjectMetadataMaps> {
|
||||
return (
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
)
|
||||
).flatObjectMetadataMaps;
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.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';
|
||||
@@ -23,7 +24,6 @@ import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
@@ -31,7 +31,7 @@ import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspa
|
||||
@Injectable()
|
||||
export class ObjectMetadataServiceV2 {
|
||||
constructor(
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
) {}
|
||||
@@ -44,9 +44,10 @@ export class ObjectMetadataServiceV2 {
|
||||
updateObjectInput: UpdateOneObjectInput;
|
||||
}): Promise<ObjectMetadataDTO> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -81,6 +82,7 @@ export class ObjectMetadataServiceV2 {
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
@@ -106,9 +108,10 @@ export class ObjectMetadataServiceV2 {
|
||||
}
|
||||
|
||||
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -143,9 +146,10 @@ export class ObjectMetadataServiceV2 {
|
||||
workspaceId: string;
|
||||
}): Promise<ObjectMetadataDTO> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -225,9 +229,10 @@ export class ObjectMetadataServiceV2 {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatObjectMetadata> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -306,9 +311,10 @@ export class ObjectMetadataServiceV2 {
|
||||
}
|
||||
|
||||
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+2
@@ -9,6 +9,7 @@ import {
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
@@ -72,6 +73,7 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
FeatureFlagModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
CoreViewModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
services: [
|
||||
ObjectMetadataService,
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
import { type ALL_FLAT_ENTITY_MAPS_PROPERTIES } from 'src/engine/core-modules/common/constant/all-flat-entity-maps-properties.constant';
|
||||
|
||||
export const WORKSPACE_FLAT_MAP_CACHE_KEY = 'workspaceFlatMapCacheKey';
|
||||
|
||||
export const WorkspaceFlatMapCache = (cacheKey: string) =>
|
||||
SetMetadata(WORKSPACE_FLAT_MAP_CACHE_KEY, cacheKey);
|
||||
export const WorkspaceFlatMapCache = (
|
||||
cacheKey: (typeof ALL_FLAT_ENTITY_MAPS_PROPERTIES)[number],
|
||||
) => SetMetadata(WORKSPACE_FLAT_MAP_CACHE_KEY, cacheKey);
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
|
||||
import { AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { WORKSPACE_FLAT_MAP_CACHE_KEY } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceFlatMapCacheRegistryService implements OnModuleInit {
|
||||
private readonly cacheServiceMap = new Map<
|
||||
keyof AllFlatEntityMaps,
|
||||
WorkspaceFlatMapCacheService<FlatEntityMaps<AllFlatEntities>>
|
||||
>();
|
||||
|
||||
constructor(private readonly discoveryService: DiscoveryService) {}
|
||||
|
||||
async onModuleInit() {
|
||||
this.discoverAndRegisterCacheServices();
|
||||
}
|
||||
|
||||
private discoverAndRegisterCacheServices(): void {
|
||||
const providers = this.discoveryService.getProviders();
|
||||
|
||||
providers.forEach((wrapper) => {
|
||||
const { instance, metatype } = wrapper;
|
||||
|
||||
if (!instance || !metatype) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheKey = Reflect.getMetadata(
|
||||
WORKSPACE_FLAT_MAP_CACHE_KEY,
|
||||
metatype,
|
||||
);
|
||||
|
||||
if (cacheKey && instance instanceof WorkspaceFlatMapCacheService) {
|
||||
this.cacheServiceMap.set(cacheKey, instance);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCacheService(
|
||||
flatEntityName: keyof AllFlatEntityMaps,
|
||||
): WorkspaceFlatMapCacheService<FlatEntityMaps<AllFlatEntities>> | undefined {
|
||||
return this.cacheServiceMap.get(flatEntityName);
|
||||
}
|
||||
}
|
||||
+15
-1
@@ -99,6 +99,7 @@ export abstract class WorkspaceFlatMapCacheService<
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
await this.beforeInvalidateCache({ workspaceId });
|
||||
const { flatMapKey, hashKey } = this.buildRemoteCacheKeys({ workspaceId });
|
||||
|
||||
await this.cacheStorageService.del(flatMapKey);
|
||||
@@ -108,6 +109,19 @@ export abstract class WorkspaceFlatMapCacheService<
|
||||
this.localCacheHashes.delete(workspaceId);
|
||||
|
||||
await this.recomputeAndStoreInCache({ workspaceId });
|
||||
await this.afterInvalidateCache({ workspaceId });
|
||||
}
|
||||
|
||||
protected async beforeInvalidateCache(_args: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
protected async afterInvalidateCache(_args: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
private getFlatMapCacheKey(): string {
|
||||
@@ -116,7 +130,7 @@ export abstract class WorkspaceFlatMapCacheService<
|
||||
this.constructor,
|
||||
);
|
||||
|
||||
if (!cacheKey) {
|
||||
if (!isDefined(cacheKey)) {
|
||||
throw new WorkspaceFlatMapCacheException(
|
||||
`${this.constructor.name} must be decorated with @WorkspaceFlatMapCache('cacheKey')`,
|
||||
WorkspaceFlatMapCacheExceptionCode.MISSING_DECORATOR,
|
||||
|
||||
+30
-4
@@ -1,10 +1,36 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { CacheStorageModule } from 'src/engine/core-modules/cache-storage/cache-storage.module';
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { WorkspaceFlatViewFieldMapCacheService } from 'src/engine/core-modules/view/flat-view/services/workspace-flat-view-field-map-cache.service';
|
||||
import { WorkspaceFlatViewMapCacheService } from 'src/engine/core-modules/view/flat-view/services/workspace-flat-view-map-cache.service';
|
||||
import { WorkspaceFlatObjectMetadataMapCacheService } from 'src/engine/metadata-modules/flat-object-metadata/services/workspace-flat-object-metadata-map-cache.service';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache-registry.service';
|
||||
|
||||
@Module({
|
||||
imports: [CacheStorageModule],
|
||||
providers: [],
|
||||
exports: [],
|
||||
imports: [
|
||||
DiscoveryModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
TypeOrmModule.forFeature([ViewEntity, ViewFieldEntity]),
|
||||
],
|
||||
providers: [
|
||||
WorkspaceFlatMapCacheRegistryService,
|
||||
WorkspaceFlatObjectMetadataMapCacheService,
|
||||
WorkspaceFlatViewMapCacheService,
|
||||
WorkspaceFlatViewFieldMapCacheService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceFlatMapCacheRegistryService,
|
||||
WorkspaceFlatObjectMetadataMapCacheService,
|
||||
WorkspaceFlatViewMapCacheService,
|
||||
WorkspaceFlatViewFieldMapCacheService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceFlatMapCacheModule {}
|
||||
|
||||
+1
-2
@@ -10,11 +10,10 @@ import { ObjectMetadataHealthService } from 'src/engine/workspace-manager/worksp
|
||||
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
||||
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
import { WorkspaceFixService } from 'src/engine/workspace-manager/workspace-health/services/workspace-fix.service';
|
||||
|
||||
import { workspaceFixers } from './fixer';
|
||||
|
||||
import { WorkspaceFixService } from './services/workspace-fix.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
DataSourceModule,
|
||||
|
||||
+1
-2
@@ -4,11 +4,10 @@ import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { WorkspaceMigrationColumnService } from 'src/engine/workspace-manager/workspace-migration-runner/services/workspace-migration-column.service';
|
||||
import { WorkspaceMigrationEnumService } from 'src/engine/workspace-manager/workspace-migration-runner/services/workspace-migration-enum.service';
|
||||
import { WorkspaceMigrationTypeService } from 'src/engine/workspace-manager/workspace-migration-runner/services/workspace-migration-type.service';
|
||||
|
||||
import { WorkspaceMigrationRunnerService } from './workspace-migration-runner.service';
|
||||
|
||||
import { WorkspaceMigrationTypeService } from './services/workspace-migration-type.service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceDataSourceModule, WorkspaceMigrationModule],
|
||||
providers: [
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities-by-metadata-engine-name.type';
|
||||
import { type FieldMetadataMinimalInformation } from 'src/engine/metadata-modules/flat-field-metadata/types/field-metadata-minimal-information.type';
|
||||
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
import { type FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
|
||||
+5
@@ -165,9 +165,14 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
};
|
||||
}
|
||||
|
||||
const relatedFlatEntityMapsKeys = Object.keys(
|
||||
fromToAllFlatEntityMaps,
|
||||
) as (keyof AllFlatEntityMaps)[];
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
workspaceMigration: {
|
||||
relatedFlatEntityMapsKeys,
|
||||
actions: allActions,
|
||||
workspaceId,
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities-by-metadata-engine-name.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { type WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
|
||||
export type ViewFieldRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps' | 'flatViewMaps'
|
||||
>;
|
||||
+1
-5
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-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 { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/view-field-related-flat-entity-maps.type';
|
||||
import {
|
||||
FlatEntityUpdateValidationArgs,
|
||||
FlatEntityValidationArgs,
|
||||
@@ -19,10 +19,6 @@ import {
|
||||
} 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 ViewFieldRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps' | 'flatViewMaps'
|
||||
>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
FlatViewField,
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-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 WorkspaceMigrationV2<
|
||||
@@ -9,5 +10,5 @@ export type WorkspaceMigrationV2<
|
||||
// description?: string;
|
||||
actions: TActions[];
|
||||
workspaceId: string;
|
||||
// objectActions: TActions[] // could be cool ?
|
||||
relatedFlatEntityMapsKeys?: (keyof AllFlatEntityMaps)[];
|
||||
};
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/view-field-related-flat-entity-maps.type';
|
||||
|
||||
type ViewFieldValidationArgs = {
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
|
||||
+28
-3
@@ -1,8 +1,13 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
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 { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
@@ -25,6 +30,22 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: action.flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateFieldAction>,
|
||||
): Promise<void> {
|
||||
@@ -42,8 +63,12 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
async executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateFieldAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, flatObjectMetadataMaps, workspaceId } =
|
||||
context;
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { flatFieldMetadata } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
|
||||
+31
-3
@@ -2,9 +2,14 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
import {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
@@ -29,6 +34,25 @@ export class DeleteFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId, objectMetadataId } = action;
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteFieldAction>,
|
||||
) {
|
||||
@@ -48,8 +72,12 @@ export class DeleteFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
async executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteFieldAction>,
|
||||
) {
|
||||
const { action, queryRunner, flatObjectMetadataMaps, workspaceId } =
|
||||
context;
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, fieldMetadataId } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
|
||||
+50
-3
@@ -4,8 +4,12 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ColumnType, type QueryRunner } from 'typeorm';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
import {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
@@ -17,7 +21,9 @@ import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-f
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
@@ -61,6 +67,43 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId, objectMetadataId } = action;
|
||||
|
||||
const existingFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMaps({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatFieldMetadata)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
`Workspace migration failed: Field metadata not found in cache`,
|
||||
WorkspaceMigrationRunnerExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedFlatFieldMetadata = {
|
||||
...existingFlatFieldMetadata,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: updatedFlatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateFieldAction>,
|
||||
): Promise<void> {
|
||||
@@ -81,8 +124,12 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
async executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateFieldAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, flatObjectMetadataMaps, workspaceId } =
|
||||
context;
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, fieldMetadataId, updates } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
|
||||
+11
-1
@@ -1,7 +1,11 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@@ -9,6 +13,12 @@ import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-ma
|
||||
export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'create_index',
|
||||
) {
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps(
|
||||
_args: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateIndexAction>,
|
||||
): Partial<AllFlatEntityMaps> {
|
||||
return {};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+11
-1
@@ -1,7 +1,11 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type DeleteIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@@ -9,6 +13,12 @@ import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-ma
|
||||
export class DeleteIndexActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'delete_index',
|
||||
) {
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps(
|
||||
_args: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteIndexAction>,
|
||||
): Partial<AllFlatEntityMaps> {
|
||||
return {};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteIndexAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+32
-3
@@ -1,13 +1,17 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type CreateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
@@ -25,11 +29,36 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
) {
|
||||
constructor(
|
||||
private readonly workspaceSchemaManagerService: WorkspaceSchemaManagerService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateObjectAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { flatObjectMetadataWithoutFields, createFieldActions } = action;
|
||||
|
||||
const flatFieldMetadatas = createFieldActions.map(
|
||||
(createFieldAction) => createFieldAction.flatFieldMetadata,
|
||||
);
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: {
|
||||
...flatObjectMetadataWithoutFields,
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas,
|
||||
},
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateObjectAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+30
-3
@@ -1,10 +1,15 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
@@ -27,6 +32,24 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteObjectAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteObjectAction>,
|
||||
): Promise<void> {
|
||||
@@ -44,8 +67,12 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
async executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteObjectAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, flatObjectMetadataMaps, workspaceId } =
|
||||
context;
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
|
||||
+42
-3
@@ -1,11 +1,17 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
@@ -29,6 +35,35 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateObjectAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const existingFlatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadata = {
|
||||
...existingFlatObjectMetadata,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateObjectAction>,
|
||||
): Promise<void> {
|
||||
@@ -48,8 +83,12 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
async executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateObjectAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, flatObjectMetadataMaps, workspaceId } =
|
||||
context;
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, updates } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
|
||||
+23
-1
@@ -1,7 +1,12 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { 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';
|
||||
@@ -14,6 +19,23 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateViewFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFieldMaps } = allFlatEntityMaps;
|
||||
const { viewField } = action;
|
||||
|
||||
const updatedFlatViewFieldMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: viewField,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewFieldMaps: updatedFlatViewFieldMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+23
-1
@@ -1,7 +1,12 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { 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';
|
||||
@@ -14,6 +19,23 @@ export class DeleteViewFieldActionHandlerService extends WorkspaceMigrationRunne
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteViewFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFieldMaps } = allFlatEntityMaps;
|
||||
const { viewFieldId } = action;
|
||||
|
||||
const updatedFlatViewFieldMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: viewFieldId,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewFieldMaps: updatedFlatViewFieldMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteViewFieldAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+34
-1
@@ -1,7 +1,13 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-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 { 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';
|
||||
@@ -15,6 +21,33 @@ export class UpdateViewFieldActionHandlerService extends WorkspaceMigrationRunne
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateViewFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFieldMaps } = allFlatEntityMaps;
|
||||
const { viewFieldId } = action;
|
||||
|
||||
const existingViewField = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: viewFieldId,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
|
||||
const updatedViewField = {
|
||||
...existingViewField,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatViewFieldMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: updatedViewField,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewFieldMaps: updatedFlatViewFieldMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateViewFieldAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+23
-1
@@ -1,7 +1,12 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { 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';
|
||||
@@ -14,6 +19,23 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateViewAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewMaps } = allFlatEntityMaps;
|
||||
const { view } = action;
|
||||
|
||||
const updatedFlatViewMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: view,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateViewAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+23
-1
@@ -1,7 +1,12 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { 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';
|
||||
@@ -14,6 +19,23 @@ export class DeleteViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteViewAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewMaps } = allFlatEntityMaps;
|
||||
const { viewId } = action;
|
||||
|
||||
const updatedFlatViewMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: viewId,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteViewAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+34
-1
@@ -1,7 +1,13 @@
|
||||
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 {
|
||||
OptimisticallyApplyActionOnAllFlatEntityMapsArgs,
|
||||
WorkspaceMigrationRunnerActionHandler,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-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 { 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';
|
||||
@@ -15,6 +21,33 @@ export class UpdateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateViewAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewMaps } = allFlatEntityMaps;
|
||||
const { viewId } = action;
|
||||
|
||||
const existingView = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: viewId,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedView = {
|
||||
...existingView,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatViewMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: updatedView,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateViewAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+1
@@ -14,4 +14,5 @@ export const WorkspaceMigrationRunnerExceptionCode = appendCommonExceptionCode({
|
||||
UNSUPPORTED_COMPOSITE_COLUMN_TYPE: 'UNSUPPORTED_COMPOSITE_COLUMN_TYPE',
|
||||
NOT_SUPPORTED: 'NOT_SUPPORTED',
|
||||
INVALID_ACTION_TYPE: 'INVALID_ACTION_TYPE',
|
||||
FLAT_ENTITY_NOT_FOUND: 'FLAT_ENTITY_NOT_FOUND',
|
||||
} as const);
|
||||
|
||||
+34
-12
@@ -1,46 +1,68 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import {
|
||||
type ExtractAction,
|
||||
type WorkspaceMigrationActionTypeV2,
|
||||
type WorkspaceMigrationActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { WORKSPACE_MIGRATION_ACTION_HANDLER_METADATA_KEY } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/constants/workspace-migration-action-handler-metadata-key.constant';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
export interface WorkspaceMigrationRunnerActionHandlerService<
|
||||
T extends WorkspaceMigrationActionTypeV2,
|
||||
TActionType extends WorkspaceMigrationActionTypeV2,
|
||||
> {
|
||||
execute(
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<T>>,
|
||||
): Promise<void>;
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<Partial<AllFlatEntityMaps>>;
|
||||
}
|
||||
|
||||
export type OptimisticallyApplyActionOnAllFlatEntityMapsArgs<
|
||||
TActionType extends WorkspaceMigrationActionV2,
|
||||
> = Pick<
|
||||
WorkspaceMigrationActionRunnerArgs<TActionType>,
|
||||
'allFlatEntityMaps' | 'action'
|
||||
>;
|
||||
|
||||
export abstract class BaseWorkspaceMigrationRunnerActionHandlerService<
|
||||
T extends WorkspaceMigrationActionTypeV2,
|
||||
> implements WorkspaceMigrationRunnerActionHandlerService<T>
|
||||
TActionType extends WorkspaceMigrationActionTypeV2,
|
||||
> implements WorkspaceMigrationRunnerActionHandlerService<TActionType>
|
||||
{
|
||||
abstract executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<T>>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<void>;
|
||||
|
||||
abstract executeForWorkspaceSchema(
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<T>>,
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<void>;
|
||||
|
||||
abstract optimisticallyApplyActionOnAllFlatEntityMaps(
|
||||
args: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<
|
||||
ExtractAction<TActionType>
|
||||
>,
|
||||
): Partial<AllFlatEntityMaps>;
|
||||
|
||||
async execute(
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<T>>,
|
||||
): Promise<void> {
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<Partial<AllFlatEntityMaps>> {
|
||||
await Promise.all([
|
||||
this.executeForMetadata(context),
|
||||
this.executeForWorkspaceSchema(context),
|
||||
]);
|
||||
|
||||
return this.optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action: context.action,
|
||||
allFlatEntityMaps: context.allFlatEntityMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function WorkspaceMigrationRunnerActionHandler<
|
||||
T extends WorkspaceMigrationActionTypeV2,
|
||||
>(actionType: T): typeof BaseWorkspaceMigrationRunnerActionHandlerService<T> {
|
||||
abstract class ActionHandlerService extends BaseWorkspaceMigrationRunnerActionHandlerService<T> {}
|
||||
TActionType extends WorkspaceMigrationActionTypeV2,
|
||||
>(
|
||||
actionType: TActionType,
|
||||
): typeof BaseWorkspaceMigrationRunnerActionHandlerService<TActionType> {
|
||||
abstract class ActionHandlerService extends BaseWorkspaceMigrationRunnerActionHandlerService<TActionType> {}
|
||||
|
||||
SetMetadata(
|
||||
WORKSPACE_MIGRATION_ACTION_HANDLER_METADATA_KEY,
|
||||
|
||||
+3
-2
@@ -3,6 +3,7 @@ import { DiscoveryService } from '@nestjs/core';
|
||||
|
||||
import { type WorkspaceMigrationRunnerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import {
|
||||
type WorkspaceMigrationActionTypeV2,
|
||||
type WorkspaceMigrationActionV2,
|
||||
@@ -54,7 +55,7 @@ export class WorkspaceMigrationRunnerActionHandlerRegistryService
|
||||
async executeActionHandler<T extends WorkspaceMigrationActionV2>(
|
||||
actionType: WorkspaceMigrationActionTypeV2,
|
||||
context: WorkspaceMigrationActionRunnerArgs<T>,
|
||||
): Promise<void> {
|
||||
): Promise<Partial<AllFlatEntityMaps>> {
|
||||
const handler = this.actionHandlers.get(actionType);
|
||||
|
||||
if (!handler) {
|
||||
@@ -64,6 +65,6 @@ export class WorkspaceMigrationRunnerActionHandlerRegistryService
|
||||
);
|
||||
}
|
||||
|
||||
await handler.execute(context);
|
||||
return await handler.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
+52
-45
@@ -3,77 +3,81 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { type WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import {
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import { WorkspaceMigrationRunnerActionHandlerRegistryService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/registry/workspace-migration-runner-action-handler-registry.service';
|
||||
import { applyWorkspaceMigrationActionOnFlatObjectMetadataMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/apply-workspace-migration-action-on-flat-object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationRunnerV2Service {
|
||||
constructor(
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
private readonly workspaceMigrationRunnerActionHandlerRegistry: WorkspaceMigrationRunnerActionHandlerRegistryService,
|
||||
) {}
|
||||
|
||||
run = async (
|
||||
workspaceMigration: WorkspaceMigrationV2,
|
||||
): Promise<FlatObjectMetadataMaps> => {
|
||||
run = async ({
|
||||
actions,
|
||||
workspaceId,
|
||||
relatedFlatEntityMapsKeys,
|
||||
}: WorkspaceMigrationV2): Promise<AllFlatEntityMaps> => {
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
let allFlatEntityMaps =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId: workspaceMigration.workspaceId,
|
||||
workspaceId,
|
||||
flatEntities: relatedFlatEntityMapsKeys,
|
||||
},
|
||||
);
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
let optimisticFlatObjectMetadataMaps = structuredClone(
|
||||
existingFlatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
let flatEntityMapsToInvalidate: (keyof AllFlatEntityMaps)[] = [];
|
||||
|
||||
try {
|
||||
for (const action of workspaceMigration.actions) {
|
||||
await this.workspaceMigrationRunnerActionHandlerRegistry.executeActionHandler(
|
||||
action.type,
|
||||
{
|
||||
action,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
queryRunner,
|
||||
workspaceId: workspaceMigration.workspaceId,
|
||||
},
|
||||
);
|
||||
for (const action of actions) {
|
||||
const partialOptimisticCache =
|
||||
await this.workspaceMigrationRunnerActionHandlerRegistry.executeActionHandler(
|
||||
action.type,
|
||||
{
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
queryRunner,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
const optimisticallyUpdatedFlatEntityMapsKeys = Object.keys(
|
||||
partialOptimisticCache,
|
||||
) as (keyof AllFlatEntityMaps)[];
|
||||
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
applyWorkspaceMigrationActionOnFlatObjectMetadataMaps({
|
||||
action,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
workspaceId: workspaceMigration.workspaceId,
|
||||
});
|
||||
flatEntityMapsToInvalidate = [
|
||||
...new Set([
|
||||
...optimisticallyUpdatedFlatEntityMapsKeys,
|
||||
...flatEntityMapsToInvalidate,
|
||||
]),
|
||||
];
|
||||
|
||||
allFlatEntityMaps = {
|
||||
...allFlatEntityMaps,
|
||||
...partialOptimisticCache,
|
||||
};
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
const { workspaceId } = workspaceMigration;
|
||||
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({
|
||||
workspaceId,
|
||||
);
|
||||
flatEntities: flatEntityMapsToInvalidate,
|
||||
});
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return optimisticFlatObjectMetadataMaps;
|
||||
return allFlatEntityMaps;
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
try {
|
||||
@@ -83,7 +87,10 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
console.trace(`Failed to rollback transaction: ${error.message}`);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
error.message,
|
||||
WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-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 WorkspaceMigrationActionRunnerArgs<
|
||||
@@ -8,6 +8,6 @@ export type WorkspaceMigrationActionRunnerArgs<
|
||||
> = {
|
||||
queryRunner: QueryRunner;
|
||||
action: T;
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
allFlatEntityMaps: AllFlatEntityMaps;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
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';
|
||||
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import {
|
||||
WorkspaceMigrationRunnerException,
|
||||
WorkspaceMigrationRunnerExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/exceptions/workspace-migration-runner.exception';
|
||||
import { type 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';
|
||||
|
||||
export const applyWorkspaceMigrationActionOnFlatObjectMetadataMaps = <
|
||||
T extends WorkspaceMigrationActionV2,
|
||||
>({
|
||||
action,
|
||||
flatObjectMetadataMaps,
|
||||
}: Omit<
|
||||
WorkspaceMigrationActionRunnerArgs<T>,
|
||||
'queryRunner'
|
||||
>): FlatObjectMetadataMaps => {
|
||||
switch (action.type) {
|
||||
case 'delete_object': {
|
||||
return deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: action.objectMetadataId,
|
||||
});
|
||||
}
|
||||
case 'create_object': {
|
||||
const flatObjectMetadataWithoutFields =
|
||||
action.flatObjectMetadataWithoutFields;
|
||||
const flatFieldMetadatas = action.createFieldActions.map(
|
||||
(createFieldAction) => createFieldAction.flatFieldMetadata,
|
||||
);
|
||||
|
||||
return addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: {
|
||||
...flatObjectMetadataWithoutFields,
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas,
|
||||
},
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
}
|
||||
case 'update_object': {
|
||||
const { objectMetadataId } = action;
|
||||
const existingFlatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMaps({
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatObjectMetadata)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
`Workspace migration failed: Object metadata not found in cache`,
|
||||
WorkspaceMigrationRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const updatedFlatObjectMetadata = {
|
||||
...existingFlatObjectMetadata,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
return replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
}
|
||||
case 'create_field': {
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: action.flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(updatedFlatObjectMetadataMaps)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
`Workspace migration failed: dispatchAndAddFlatFieldMetadataInFlatObjectMetadataMaps failed`,
|
||||
WorkspaceMigrationRunnerExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return updatedFlatObjectMetadataMaps;
|
||||
}
|
||||
case 'update_field': {
|
||||
const { fieldMetadataId, objectMetadataId } = action;
|
||||
const existingFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMaps({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatFieldMetadata)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
`Workspace migration failed: Field metadata not found in cache`,
|
||||
WorkspaceMigrationRunnerExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedFlatFieldMetadata = {
|
||||
...existingFlatFieldMetadata,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: updatedFlatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(updatedFlatObjectMetadataMaps)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
`Workspace migration failed: dispatchAndReplaceFlatFieldMetadataInFlatObjectMetadataMaps failed`,
|
||||
WorkspaceMigrationRunnerExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return updatedFlatObjectMetadataMaps;
|
||||
}
|
||||
case 'delete_field': {
|
||||
return deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: action.fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: action.objectMetadataId,
|
||||
});
|
||||
}
|
||||
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(
|
||||
action,
|
||||
'Should never occur, encountered an unsupported workspace migration action type',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
+2
-4
@@ -2,11 +2,10 @@ import { Module } from '@nestjs/common';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceSchemaMigrationRunnerActionHandlersModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module';
|
||||
import { WorkspaceMigrationRunnerActionHandlerRegistryService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/registry/workspace-migration-runner-action-handler-registry.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/services/workspace-migration-runner-v2.service';
|
||||
@@ -16,10 +15,9 @@ import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/
|
||||
FeatureFlagModule,
|
||||
TypeORMModule,
|
||||
DataSourceModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceSchemaMigrationRunnerActionHandlersModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
DiscoveryModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
-2
@@ -1,6 +1,5 @@
|
||||
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';
|
||||
@@ -12,7 +11,6 @@ import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/w
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
WorkspaceMetadataCacheModule,
|
||||
ViewCacheModule,
|
||||
],
|
||||
providers: [
|
||||
WorkspaceMigrationValidateBuildAndRunService,
|
||||
|
||||
+3
-3
@@ -1,15 +1,15 @@
|
||||
import { createOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/create-one-core-view-field.util';
|
||||
import { deleteOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/delete-one-core-view-field.util';
|
||||
import { destroyOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/destroy-one-core-view-field.util';
|
||||
import { assertViewFieldStructure } from 'test/integration/utils/view-test.util';
|
||||
import {
|
||||
type EachTestingContext,
|
||||
eachTestingContextFilter,
|
||||
} from 'twenty-shared/testing';
|
||||
import { deleteOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/delete-one-core-view-field.util';
|
||||
import { destroyOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/destroy-one-core-view-field.util';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type ViewFieldDTO } from 'src/engine/core-modules/view/dtos/view-field.dto';
|
||||
import { type CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
import { type ViewFieldDTO } from 'src/engine/core-modules/view/dtos/view-field.dto';
|
||||
|
||||
import {
|
||||
cleanupViewFieldTestV2,
|
||||
|
||||
+13
-5
@@ -1,10 +1,10 @@
|
||||
import { createTestViewWithGraphQL } from 'test/integration/graphql/utils/view-graphql.util';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
|
||||
@@ -57,13 +57,21 @@ export const setupViewFieldTestV2 = async (): Promise<ViewFieldTestSetup> => {
|
||||
`,
|
||||
});
|
||||
|
||||
const view = await createTestViewWithGraphQL({
|
||||
name: 'Test View for Fields',
|
||||
objectMetadataId,
|
||||
const {
|
||||
data: {
|
||||
createCoreView: { id: testViewId },
|
||||
},
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
icon: 'icon123',
|
||||
objectMetadataId,
|
||||
name: 'TestViewForFields',
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
return {
|
||||
testViewId: view.id,
|
||||
testViewId,
|
||||
testObjectMetadataId: objectMetadataId,
|
||||
testFieldMetadataId: fieldMetadataId,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user