[GQL_VIEW_FILTER_API_BREAKING_CHANGE][WHEN_RELEASED_REQUIRES_CACHE_FLUSH] ViewFilter migration to workspace migration v2 (#15010)
# Introduction Migrating `viewFilter` to v2 in order to migrate later the field update side effect on view to v2 too ## What's done - Created flat-view-filter - flat view filter runner - flat view filter builder - create view filter service v2 and input transpilers - refactor the existing view filter resolver to fix standard ( BREAKING_CHANGE on graphql api update especially ) REST stays the same - refactored the front to consume the mutations autogenerated ## New generic tools ### Compare two flat entity Introducing a new util to compare two flat entity, it's strictly typed and will be added to the generic builder in a following PR This will ease flat entity addition as won't required to create a specific abstraction for comparison Generic builder will expect specific constant: properties to compare and properties to stringify ### Transform flat entity for comparison Forked and refactor the initial existing method for flat entity business scope and type safety ## Coverage Migrated existing integration tests to fit new contract API This PR does not add strong coverage on validation exceptions Deadlines are too short close https://github.com/twentyhq/core-team-issues/issues/1666
This commit is contained in:
+1
@@ -10,4 +10,5 @@ export const ALL_FLAT_ENTITY_MAPS_PROPERTIES = [
|
||||
'flatCronTriggerMaps',
|
||||
'flatRouteTriggerMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatViewFilterMaps',
|
||||
] as const satisfies (keyof AllFlatEntityMaps)[];
|
||||
|
||||
+1
@@ -11,4 +11,5 @@ export const EMPTY_ALL_FLAT_ENTITY_MAPS = {
|
||||
flatCronTriggerMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
flatDatabaseEventTriggerMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
flatRouteTriggerMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
flatViewFilterMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
} as const satisfies AllFlatEntityMaps;
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { type FlatRouteTrigger } from 'src/engine/metadata-modules/route-trigger/types/flat-route-trigger.type';
|
||||
import { type FlatServerlessFunction } from 'src/engine/metadata-modules/serverless-function/types/flat-serverless-function.type';
|
||||
@@ -18,4 +19,5 @@ export type AllFlatEntitiesByMetadataEngineName = {
|
||||
cronTrigger: FlatCronTrigger;
|
||||
databaseEventTrigger: FlatDatabaseEventTrigger;
|
||||
routeTrigger: FlatRouteTrigger;
|
||||
viewFilter: FlatViewFilter;
|
||||
};
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import diff from 'microdiff';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { parseJson } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { transformFlatEntityForComparison } from 'src/engine/core-modules/common/utils/transform-flat-entity-for-comparison.util';
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
|
||||
export const compareTwoFlatEntity = <
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
PToCompare extends keyof TFlatEntity,
|
||||
PJsonB extends keyof TFlatEntity,
|
||||
>({
|
||||
fromFlatEntity,
|
||||
toFlatEntity,
|
||||
propertiesToCompare,
|
||||
propertiesToStringify,
|
||||
}: {
|
||||
propertiesToCompare: PToCompare[];
|
||||
propertiesToStringify: PJsonB[];
|
||||
} & FromTo<TFlatEntity, 'flatEntity'>) => {
|
||||
const [transformedFromFlatEntity, transformedToFlatEntity] = [
|
||||
fromFlatEntity,
|
||||
toFlatEntity,
|
||||
].map((flatEntity) =>
|
||||
transformFlatEntityForComparison({
|
||||
flatEntity,
|
||||
options: {
|
||||
propertiesToCompare,
|
||||
propertiesToStringify,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const flatEntityDifferences = diff(
|
||||
transformedFromFlatEntity,
|
||||
transformedToFlatEntity,
|
||||
);
|
||||
|
||||
return flatEntityDifferences.flatMap<
|
||||
Array<
|
||||
{
|
||||
[P in PToCompare]: PropertyUpdate<TFlatEntity, P>;
|
||||
}[PToCompare]
|
||||
>[number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as PToCompare;
|
||||
const isJsonb = propertiesToStringify.includes(
|
||||
property as unknown as PJsonB,
|
||||
);
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: parseJson(oldValue),
|
||||
to: parseJson(value),
|
||||
property,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occurs, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { type AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { orderObjectProperties } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/order-object-properties.util';
|
||||
|
||||
export function transformFlatEntityForComparison<
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
PToCompare extends keyof TFlatEntity,
|
||||
PJsonB extends keyof TFlatEntity,
|
||||
>({
|
||||
flatEntity,
|
||||
options: { propertiesToCompare, propertiesToStringify },
|
||||
}: {
|
||||
flatEntity: TFlatEntity;
|
||||
options: {
|
||||
propertiesToCompare: PToCompare[];
|
||||
propertiesToStringify: PJsonB[];
|
||||
};
|
||||
}) {
|
||||
return propertiesToCompare.reduce(
|
||||
(flatEntityAccumulator, propertyToCompare) => {
|
||||
const currentValue = flatEntity[propertyToCompare];
|
||||
|
||||
if (
|
||||
propertiesToStringify.includes(propertyToCompare as unknown as PJsonB)
|
||||
) {
|
||||
const orderedValue = orderObjectProperties(currentValue);
|
||||
|
||||
return {
|
||||
...flatEntityAccumulator,
|
||||
[propertyToCompare]: JSON.stringify(orderedValue),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...flatEntityAccumulator,
|
||||
[propertyToCompare]: currentValue,
|
||||
};
|
||||
},
|
||||
{} as Pick<TFlatEntity, PToCompare>,
|
||||
);
|
||||
}
|
||||
+1
@@ -24,6 +24,7 @@ export const throwOnFieldInputTranspilationsError: ThrowOnFieldInputTranspilatio
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
{
|
||||
report: {
|
||||
viewFilter: [],
|
||||
fieldMetadata: [],
|
||||
index: [],
|
||||
view: [],
|
||||
|
||||
+5
-42
@@ -1,11 +1,8 @@
|
||||
import diff from 'microdiff';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { compareTwoFlatEntity } from 'src/engine/core-modules/common/utils/compare-two-flat-entity.util';
|
||||
import { FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-view-field/constants/flat-view-field-properties-to-compare.constant';
|
||||
import { type FlatViewFieldPropertiesToCompare } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field-properties-to-compare.type';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { type UpdateViewFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
type GetWorkspaceMigrationUpdateViewFieldActionArgs = FromTo<
|
||||
FlatViewField,
|
||||
@@ -16,44 +13,10 @@ export const compareTwoFlatViewField = ({
|
||||
fromFlatViewField,
|
||||
toFlatViewField,
|
||||
}: GetWorkspaceMigrationUpdateViewFieldActionArgs) => {
|
||||
const transformMetadataForComparisonParameters = {
|
||||
shouldIgnoreProperty: (property: string) =>
|
||||
!FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE.includes(
|
||||
property as FlatViewFieldPropertiesToCompare,
|
||||
),
|
||||
return compareTwoFlatEntity({
|
||||
fromFlatEntity: fromFlatViewField,
|
||||
toFlatEntity: toFlatViewField,
|
||||
propertiesToStringify: [],
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
fromFlatViewField,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
toFlatViewField,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
|
||||
const flatViewFieldDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
return flatViewFieldDifferences.flatMap<
|
||||
UpdateViewFieldAction['updates'][number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatViewFieldPropertiesToCompare;
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occurs, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
}
|
||||
propertiesToCompare: FLAT_VIEW_FIELD_PROPERTIES_TO_COMPARE,
|
||||
});
|
||||
};
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
export const FLAT_VIEW_FILTER_EDITABLE_PROPERTIES = [
|
||||
'fieldMetadataId',
|
||||
'operand',
|
||||
'value',
|
||||
'viewFilterGroupId',
|
||||
'positionInViewFilterGroup',
|
||||
'subFieldName',
|
||||
] as const satisfies (keyof FlatViewFilter)[];
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
export const FLAT_VIEW_FILTER_JSONB_PROPERTIES = [
|
||||
'value',
|
||||
] as const satisfies (keyof FlatViewFilter)[];
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
export const FLAT_VIEW_FILTER_PROPERTIES_TO_COMPARE = [
|
||||
'viewId',
|
||||
'deletedAt',
|
||||
...FLAT_VIEW_FILTER_EDITABLE_PROPERTIES,
|
||||
] as const satisfies (keyof FlatViewFilter)[];
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { type ViewFilterEntityRelationProperties } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
export const VIEW_FILTER_ENTITY_RELATION_PROPERTIES = [
|
||||
'fieldMetadata',
|
||||
'view',
|
||||
'viewFilterGroup',
|
||||
'workspace',
|
||||
] as const satisfies ViewFilterEntityRelationProperties[];
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { type FlatViewFilterMaps } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-maps.type';
|
||||
import { fromViewFilterEntityToFlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/utils/from-view-filter-entity-to-flat-view-filter.util';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatViewFilterMaps')
|
||||
export class WorkspaceFlatViewFilterMapCacheService extends WorkspaceFlatMapCacheService<FlatViewFilterMaps> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
@InjectRepository(ViewFilterEntity)
|
||||
private readonly viewFilterRepository: Repository<ViewFilterEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatViewFilterMaps> {
|
||||
const viewFilters = await this.viewFilterRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
return viewFilters.reduce((flatViewFilterMaps, viewFilterEntity) => {
|
||||
const flatViewFilter =
|
||||
fromViewFilterEntityToFlatViewFilter(viewFilterEntity);
|
||||
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFilter,
|
||||
flatEntityMaps: flatViewFilterMaps,
|
||||
});
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
export type FlatViewFilterMaps = FlatEntityMaps<FlatViewFilter>;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FLAT_VIEW_FILTER_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-properties-to-compare.constant';
|
||||
|
||||
export type FlatViewFilterPropertiesToCompare =
|
||||
(typeof FLAT_VIEW_FILTER_PROPERTIES_TO_COMPARE)[number];
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { type Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
|
||||
import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
|
||||
export type ViewFilterEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
ViewFilterEntity,
|
||||
FieldMetadataEntity | ViewEntity | ViewFilterGroupEntity | Workspace
|
||||
>;
|
||||
|
||||
export type FlatViewFilter = Omit<
|
||||
ViewFilterEntity,
|
||||
ViewFilterEntityRelationProperties
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { compareTwoFlatEntity } from 'src/engine/core-modules/common/utils/compare-two-flat-entity.util';
|
||||
import { FLAT_VIEW_FILTER_JSONB_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-jsonb-properties.constant';
|
||||
import { FLAT_VIEW_FILTER_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-properties-to-compare.constant';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
|
||||
type GetWorkspaceMigrationUpdateViewFilterActionArgs = FromTo<
|
||||
FlatViewFilter,
|
||||
'FlatViewFilter'
|
||||
>;
|
||||
|
||||
export const compareTwoFlatViewFilter = ({
|
||||
fromFlatViewFilter,
|
||||
toFlatViewFilter,
|
||||
}: GetWorkspaceMigrationUpdateViewFilterActionArgs) => {
|
||||
return compareTwoFlatEntity({
|
||||
fromFlatEntity: fromFlatViewFilter,
|
||||
toFlatEntity: toFlatViewFilter,
|
||||
propertiesToStringify: FLAT_VIEW_FILTER_JSONB_PROPERTIES,
|
||||
propertiesToCompare: FLAT_VIEW_FILTER_PROPERTIES_TO_COMPARE,
|
||||
});
|
||||
};
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type CreateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/create-view-filter.input';
|
||||
|
||||
export const fromCreateViewFilterInputToFlatViewFilterToCreate = ({
|
||||
createViewFilterInput: rawCreateViewFilterInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
createViewFilterInput: CreateViewFilterInput;
|
||||
workspaceId: string;
|
||||
}): FlatViewFilter => {
|
||||
const { fieldMetadataId, viewId, value, ...createViewFilterInput } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawCreateViewFilterInput,
|
||||
[
|
||||
'fieldMetadataId',
|
||||
'id',
|
||||
'viewId',
|
||||
'viewFilterGroupId',
|
||||
'operand',
|
||||
'subFieldName',
|
||||
],
|
||||
);
|
||||
|
||||
const createdAt = new Date();
|
||||
const viewFilterId = createViewFilterInput.id ?? v4();
|
||||
|
||||
return {
|
||||
id: viewFilterId,
|
||||
fieldMetadataId,
|
||||
viewId,
|
||||
workspaceId,
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
universalIdentifier: viewFilterId,
|
||||
operand: createViewFilterInput.operand ?? ViewFilterOperand.CONTAINS,
|
||||
value: value,
|
||||
viewFilterGroupId: createViewFilterInput.viewFilterGroupId ?? null,
|
||||
positionInViewFilterGroup:
|
||||
createViewFilterInput.positionInViewFilterGroup ?? null,
|
||||
subFieldName: createViewFilterInput.subFieldName ?? null,
|
||||
};
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatViewFilterMaps } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-maps.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type DeleteViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/delete-view-filter.input';
|
||||
import {
|
||||
ViewFilterException,
|
||||
ViewFilterExceptionCode,
|
||||
} from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
|
||||
|
||||
export const fromDeleteViewFilterInputToFlatViewFilterOrThrow = ({
|
||||
deleteViewFilterInput: rawDeleteViewFilterInput,
|
||||
flatViewFilterMaps,
|
||||
}: {
|
||||
deleteViewFilterInput: DeleteViewFilterInput;
|
||||
flatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FlatViewFilter => {
|
||||
const { id: viewFilterId } = extractAndSanitizeObjectStringFields(
|
||||
rawDeleteViewFilterInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewFilterToDelete = flatViewFilterMaps.byId[viewFilterId];
|
||||
|
||||
if (!isDefined(existingFlatViewFilterToDelete)) {
|
||||
throw new ViewFilterException(
|
||||
t`View filter to delete not found`,
|
||||
ViewFilterExceptionCode.VIEW_FILTER_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...existingFlatViewFilterToDelete,
|
||||
deletedAt: new Date(),
|
||||
};
|
||||
};
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatViewFilterMaps } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-maps.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type DestroyViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/destroy-view-filter.input';
|
||||
import {
|
||||
ViewFilterException,
|
||||
ViewFilterExceptionCode,
|
||||
} from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
|
||||
|
||||
export const fromDestroyViewFilterInputToFlatViewFilterOrThrow = ({
|
||||
destroyViewFilterInput,
|
||||
flatViewFilterMaps,
|
||||
}: {
|
||||
destroyViewFilterInput: DestroyViewFilterInput;
|
||||
flatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FlatViewFilter => {
|
||||
const { id: viewFilterId } = extractAndSanitizeObjectStringFields(
|
||||
destroyViewFilterInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewFilterToDestroy = flatViewFilterMaps.byId[viewFilterId];
|
||||
|
||||
if (!isDefined(existingFlatViewFilterToDestroy)) {
|
||||
throw new ViewFilterException(
|
||||
t`View filter to destroy not found`,
|
||||
ViewFilterExceptionCode.VIEW_FILTER_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return existingFlatViewFilterToDestroy;
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant';
|
||||
import { type FlatViewFilterMaps } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-maps.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type UpdateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/update-view-filter.input';
|
||||
import {
|
||||
ViewFilterException,
|
||||
ViewFilterExceptionCode,
|
||||
} from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
|
||||
export const fromUpdateViewFilterInputToFlatViewFilterToUpdateOrThrow = ({
|
||||
updateViewFilterInput: rawUpdateViewFilterInput,
|
||||
flatViewFilterMaps,
|
||||
}: {
|
||||
updateViewFilterInput: UpdateViewFilterInput;
|
||||
flatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FlatViewFilter => {
|
||||
const { id: viewFilterToUpdateId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateViewFilterInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatViewFilterToUpdate =
|
||||
flatViewFilterMaps.byId[viewFilterToUpdateId];
|
||||
|
||||
if (!isDefined(existingFlatViewFilterToUpdate)) {
|
||||
throw new ViewFilterException(
|
||||
t`View filter to update not found`,
|
||||
ViewFilterExceptionCode.VIEW_FILTER_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateViewFilterInput.update,
|
||||
FLAT_VIEW_FILTER_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
return mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewFilterToUpdate,
|
||||
properties: FLAT_VIEW_FILTER_EDITABLE_PROPERTIES,
|
||||
update: updatedEditableFieldProperties,
|
||||
});
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { VIEW_FILTER_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/view-filter-entity-relation-properties.constant';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
|
||||
export const fromViewFilterEntityToFlatViewFilter = (
|
||||
viewFilterEntity: ViewFilterEntity,
|
||||
): FlatViewFilter => {
|
||||
const viewFilterEntityWithoutRelations = removePropertiesFromRecord(
|
||||
viewFilterEntity,
|
||||
VIEW_FILTER_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
|
||||
return {
|
||||
...viewFilterEntityWithoutRelations,
|
||||
universalIdentifier:
|
||||
viewFilterEntityWithoutRelations.universalIdentifier ??
|
||||
viewFilterEntityWithoutRelations.id,
|
||||
};
|
||||
};
|
||||
+4
-1
@@ -39,8 +39,11 @@ export class WorkspaceFlatViewMapCacheService extends WorkspaceFlatMapCacheServi
|
||||
viewFields: {
|
||||
id: true,
|
||||
},
|
||||
viewFilters: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
relations: ['viewFields'],
|
||||
relations: ['viewFields', 'viewFilters'],
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -23,4 +23,5 @@ export type ViewEntityRelationProperties =
|
||||
export type FlatView = Omit<ViewEntity, ViewEntityRelationProperties> & {
|
||||
universalIdentifier: string;
|
||||
viewFieldIds: string[];
|
||||
viewFilterIds: string[];
|
||||
};
|
||||
|
||||
+1
@@ -44,5 +44,6 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
type: createViewInput.type ?? ViewType.TABLE,
|
||||
universalIdentifier: v4(),
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
};
|
||||
};
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewOpenRecordIn } from 'src/engine/metadata-modules/view/enums/view-open-record-in';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
|
||||
export const fromPartialFlatViewToFlatViewWithDefault = (
|
||||
partialFlatView: Partial<FlatView>,
|
||||
): FlatView => {
|
||||
const createdAt = new Date();
|
||||
const viewId = partialFlatView.id ?? v4();
|
||||
|
||||
return {
|
||||
...partialFlatView,
|
||||
id: viewId,
|
||||
name: partialFlatView.name ?? '',
|
||||
objectMetadataId: partialFlatView.objectMetadataId ?? '',
|
||||
type: partialFlatView.type ?? ViewType.TABLE,
|
||||
key: partialFlatView.key ?? null,
|
||||
icon: partialFlatView.icon ?? '',
|
||||
position: partialFlatView.position ?? 0,
|
||||
isCompact: partialFlatView.isCompact ?? false,
|
||||
isCustom: partialFlatView.isCustom ?? false,
|
||||
openRecordIn: partialFlatView.openRecordIn ?? ViewOpenRecordIn.SIDE_PANEL,
|
||||
kanbanAggregateOperation: partialFlatView.kanbanAggregateOperation ?? null,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
partialFlatView.kanbanAggregateOperationFieldMetadataId ?? null,
|
||||
workspaceId: partialFlatView.workspaceId ?? '',
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
anyFieldFilterValue: partialFlatView.anyFieldFilterValue ?? null,
|
||||
universalIdentifier: partialFlatView.universalIdentifier ?? viewId,
|
||||
viewFieldIds: partialFlatView.viewFieldIds ?? [],
|
||||
calendarLayout: partialFlatView.calendarLayout ?? null,
|
||||
calendarFieldMetadataId: partialFlatView.calendarFieldMetadataId ?? null,
|
||||
};
|
||||
};
|
||||
+2
-1
@@ -15,6 +15,7 @@ export const fromViewEntityToFlatView = (viewEntity: ViewEntity): FlatView => {
|
||||
universalIdentifier:
|
||||
viewEntityWithoutRelations.universalIdentifier ??
|
||||
viewEntityWithoutRelations.id,
|
||||
viewFieldIds: viewEntity.viewFields.map((viewField) => viewField.id) ?? [],
|
||||
viewFieldIds: viewEntity.viewFields.map((viewField) => viewField.id),
|
||||
viewFilterIds: viewEntity.viewFilters.map((viewFilter) => viewFilter.id),
|
||||
};
|
||||
};
|
||||
|
||||
+7
-2
@@ -89,10 +89,15 @@ export class ViewFilterController {
|
||||
@Body() input: UpdateViewFilterInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<ViewFilterDTO> {
|
||||
const updatedViewFilter = await this.viewFilterService.update(
|
||||
const updateInput: UpdateViewFilterInput = {
|
||||
id,
|
||||
update: input.update ?? input,
|
||||
};
|
||||
|
||||
const updatedViewFilter = await this.viewFilterService.update(
|
||||
updateInput.id,
|
||||
workspace.id,
|
||||
input,
|
||||
updateInput.update,
|
||||
);
|
||||
|
||||
return updatedViewFilter;
|
||||
|
||||
+21
@@ -2,33 +2,54 @@ import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import {
|
||||
IsDefined,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewFilterValue } from 'src/engine/metadata-modules/view-filter/types/view-filter-value.type';
|
||||
|
||||
@InputType()
|
||||
export class CreateViewFilterInput {
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id?: string;
|
||||
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
fieldMetadataId: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewFilterOperand)
|
||||
@Field({ nullable: true, defaultValue: ViewFilterOperand.CONTAINS })
|
||||
operand?: ViewFilterOperand;
|
||||
|
||||
@IsDefined()
|
||||
@Field(() => GraphQLJSON, { nullable: false })
|
||||
value: ViewFilterValue;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
viewFilterGroupId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Field({ nullable: true })
|
||||
positionInViewFilterGroup?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
subFieldName?: string;
|
||||
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
viewId: string;
|
||||
}
|
||||
|
||||
+15
@@ -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 DeleteViewFilterInput {
|
||||
@IDField(() => UUIDScalarType, {
|
||||
description: 'The id of the view filter to delete.',
|
||||
})
|
||||
@IsUUID()
|
||||
id: string;
|
||||
}
|
||||
+15
@@ -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 DestroyViewFilterInput {
|
||||
@IDField(() => UUIDScalarType, {
|
||||
description: 'The id of the view filter to destroy.',
|
||||
})
|
||||
@IsUUID()
|
||||
id: string;
|
||||
}
|
||||
+63
-3
@@ -1,6 +1,66 @@
|
||||
import { InputType, PartialType } from '@nestjs/graphql';
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { CreateViewFilterInput } from './create-view-filter.input';
|
||||
import {
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewFilterValue } from 'src/engine/metadata-modules/view-filter/types/view-filter-value.type';
|
||||
|
||||
@InputType()
|
||||
export class UpdateViewFilterInput extends PartialType(CreateViewFilterInput) {}
|
||||
class UpdateViewFilterInputUpdates {
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
fieldMetadataId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ViewFilterOperand)
|
||||
@Field({ nullable: true })
|
||||
operand?: ViewFilterOperand;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
value?: ViewFilterValue;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
viewFilterGroupId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Field({ nullable: true })
|
||||
positionInViewFilterGroup?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
subFieldName?: string;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class UpdateViewFilterInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType, {
|
||||
description: 'The id of the view filter to update',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@Type(() => UpdateViewFilterInputUpdates)
|
||||
@ValidateNested()
|
||||
@Field(() => UpdateViewFilterInputUpdates, {
|
||||
description: 'The view filter to update',
|
||||
})
|
||||
update: UpdateViewFilterInputUpdates;
|
||||
}
|
||||
|
||||
+7
-4
@@ -26,7 +26,10 @@ import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entit
|
||||
where: '"deletedAt" IS NULL',
|
||||
})
|
||||
@Index('IDX_VIEW_FILTER_FIELD_METADATA_ID', ['fieldMetadataId'])
|
||||
export class ViewFilterEntity extends SyncableEntity {
|
||||
export class ViewFilterEntity
|
||||
extends SyncableEntity
|
||||
implements Required<ViewFilterEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@@ -51,13 +54,13 @@ export class ViewFilterEntity extends SyncableEntity {
|
||||
value: ViewFilterValue;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
viewFilterGroupId?: string | null;
|
||||
viewFilterGroupId: string | null;
|
||||
|
||||
@Column({ nullable: true, type: 'double precision' })
|
||||
positionInViewFilterGroup: number | null;
|
||||
|
||||
@Column({ nullable: true, type: 'text', default: null })
|
||||
subFieldName?: string | null;
|
||||
subFieldName: string | null;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
viewId: string;
|
||||
@@ -72,7 +75,7 @@ export class ViewFilterEntity extends SyncableEntity {
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({ type: 'timestamptz' })
|
||||
deletedAt?: Date | null;
|
||||
deletedAt: Date | null;
|
||||
|
||||
@ManyToOne(() => Workspace, {
|
||||
onDelete: 'CASCADE',
|
||||
|
||||
+90
-26
@@ -1,14 +1,17 @@
|
||||
import { UseFilters, UseGuards } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
|
||||
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 { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { CreateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/create-view-filter.input';
|
||||
import { DeleteViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/delete-view-filter.input';
|
||||
import { DestroyViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/destroy-view-filter.input';
|
||||
import { UpdateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/update-view-filter.input';
|
||||
import { ViewFilterDTO } from 'src/engine/metadata-modules/view-filter/dtos/view-filter.dto';
|
||||
import { ViewFilterV2Service } from 'src/engine/metadata-modules/view-filter/services/view-filter-v2.service';
|
||||
import { ViewFilterService } from 'src/engine/metadata-modules/view-filter/services/view-filter.service';
|
||||
import { ViewGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view/utils/view-graphql-api-exception.filter';
|
||||
|
||||
@@ -16,7 +19,11 @@ import { ViewGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view/
|
||||
@UseFilters(ViewGraphqlApiExceptionFilter)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
export class ViewFilterResolver {
|
||||
constructor(private readonly viewFilterService: ViewFilterService) {}
|
||||
constructor(
|
||||
private readonly viewFilterService: ViewFilterService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly viewFilterV2Service: ViewFilterV2Service,
|
||||
) {}
|
||||
|
||||
@Query(() => [ViewFilterDTO])
|
||||
async getCoreViewFilters(
|
||||
@@ -41,47 +48,104 @@ export class ViewFilterResolver {
|
||||
|
||||
@Mutation(() => ViewFilterDTO)
|
||||
async createCoreViewFilter(
|
||||
@Args('input') input: CreateViewFilterInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args('input') createViewFilterInput: CreateViewFilterInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ViewFilterDTO> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.viewFilterV2Service.createOne({
|
||||
createViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
return this.viewFilterService.create({
|
||||
...input,
|
||||
workspaceId: workspace.id,
|
||||
...createViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => ViewFilterDTO)
|
||||
async updateCoreViewFilter(
|
||||
@Args('id', { type: () => String }) id: string,
|
||||
@Args('input') input: UpdateViewFilterInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args('input') updateViewFilterInput: UpdateViewFilterInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ViewFilterDTO> {
|
||||
return this.viewFilterService.update(id, workspace.id, input);
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.viewFilterV2Service.updateOne({
|
||||
updateViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
return this.viewFilterService.update(
|
||||
updateViewFilterInput.id,
|
||||
workspaceId,
|
||||
updateViewFilterInput.update,
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
@Mutation(() => ViewFilterDTO)
|
||||
async deleteCoreViewFilter(
|
||||
@Args('id', { type: () => String }) id: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<boolean> {
|
||||
@Args('input') deleteViewFilterInput: DeleteViewFilterInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ViewFilterDTO> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.viewFilterV2Service.deleteOne({
|
||||
deleteViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const deletedViewFilter = await this.viewFilterService.delete(
|
||||
id,
|
||||
workspace.id,
|
||||
deleteViewFilterInput.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return isDefined(deletedViewFilter);
|
||||
return deletedViewFilter;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
@Mutation(() => ViewFilterDTO)
|
||||
async destroyCoreViewFilter(
|
||||
@Args('id', { type: () => String }) id: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<boolean> {
|
||||
const deletedViewFilter = await this.viewFilterService.destroy(
|
||||
id,
|
||||
workspace.id,
|
||||
@Args('input') destroyViewFilterInput: DestroyViewFilterInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ViewFilterDTO> {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
const destroyedViewFilter = await this.viewFilterV2Service.destroyOne({
|
||||
destroyViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return destroyedViewFilter;
|
||||
}
|
||||
|
||||
const destroyedViewFilter = await this.viewFilterService.destroy(
|
||||
destroyViewFilterInput.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return isDefined(deletedViewFilter);
|
||||
return destroyedViewFilter;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -333,7 +333,7 @@ describe('ViewFilterService', () => {
|
||||
|
||||
expect(viewFilterService.findById).toHaveBeenCalledWith(id, workspaceId);
|
||||
expect(viewFilterRepository.delete).toHaveBeenCalledWith(id);
|
||||
expect(result).toEqual(true);
|
||||
expect(result).toEqual(mockViewFilter);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
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 { fromCreateViewFilterInputToFlatViewFilterToCreate } from 'src/engine/metadata-modules/flat-view-filter/utils/from-create-view-filter-input-to-flat-view-filter-to-create.util';
|
||||
import { fromDeleteViewFilterInputToFlatViewFilterOrThrow } from 'src/engine/metadata-modules/flat-view-filter/utils/from-delete-view-filter-input-to-flat-view-filter-or-throw.util';
|
||||
import { fromDestroyViewFilterInputToFlatViewFilterOrThrow } from 'src/engine/metadata-modules/flat-view-filter/utils/from-destroy-view-filter-input-to-flat-view-filter-or-throw.util';
|
||||
import { fromUpdateViewFilterInputToFlatViewFilterToUpdateOrThrow } from 'src/engine/metadata-modules/flat-view-filter/utils/from-update-view-filter-input-to-flat-view-filter-to-update-or-throw.util';
|
||||
import { CreateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/create-view-filter.input';
|
||||
import { DeleteViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/delete-view-filter.input';
|
||||
import { DestroyViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/destroy-view-filter.input';
|
||||
import { UpdateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/update-view-filter.input';
|
||||
import { ViewFilterDTO } from 'src/engine/metadata-modules/view-filter/dtos/view-filter.dto';
|
||||
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 ViewFilterV2Service {
|
||||
constructor(
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
async createOne({
|
||||
createViewFilterInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
createViewFilterInput: CreateViewFilterInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFilterDTO> {
|
||||
const {
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFilterMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatObjectMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const flatViewFilterToCreate =
|
||||
fromCreateViewFilterInputToFlatViewFilterToCreate({
|
||||
createViewFilterInput,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const toFlatViewFilterMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewFilterToCreate,
|
||||
flatEntityMaps: existingFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewFilterMaps: {
|
||||
from: existingFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatFieldMetadataMaps,
|
||||
flatViewMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating view filter',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatViewFilterMaps: recomputedExistingFlatViewFilterMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFilterMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFilterToCreate.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewFilterMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async updateOne({
|
||||
updateViewFilterInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
updateViewFilterInput: UpdateViewFilterInput;
|
||||
}): Promise<ViewFilterDTO> {
|
||||
const {
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFilterMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatViewFilter =
|
||||
fromUpdateViewFilterInputToFlatViewFilterToUpdateOrThrow({
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
updateViewFilterInput,
|
||||
});
|
||||
|
||||
const toFlatViewFilterMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: optimisticallyUpdatedFlatViewFilter,
|
||||
flatEntityMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewFilterMaps: {
|
||||
from: existingFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating view filter',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatViewFilterMaps: recomputedExistingFlatViewFilterMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFilterMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: optimisticallyUpdatedFlatViewFilter.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewFilterMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOne({
|
||||
deleteViewFilterInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
deleteViewFilterInput: DeleteViewFilterInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFilterDTO> {
|
||||
const {
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFilterMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatViewFilterWithDeletedAt =
|
||||
fromDeleteViewFilterInputToFlatViewFilterOrThrow({
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
deleteViewFilterInput,
|
||||
});
|
||||
|
||||
const toFlatViewFilterMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: optimisticallyUpdatedFlatViewFilterWithDeletedAt,
|
||||
flatEntityMaps: existingFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewFilterMaps: {
|
||||
from: existingFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatFieldMetadataMaps,
|
||||
flatViewMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while deleting view filter',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatViewFilterMaps: recomputedExistingFlatViewFilterMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFilterMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: optimisticallyUpdatedFlatViewFilterWithDeletedAt.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewFilterMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async destroyOne({
|
||||
destroyViewFilterInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
destroyViewFilterInput: DestroyViewFilterInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFilterDTO> {
|
||||
const {
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFilterMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const existingViewFilterToDelete =
|
||||
fromDestroyViewFilterInputToFlatViewFilterOrThrow({
|
||||
destroyViewFilterInput,
|
||||
flatViewFilterMaps: existingFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
const fromFlatViewFilterMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: [existingViewFilterToDelete.id],
|
||||
flatEntityMaps: existingFlatViewFilterMaps,
|
||||
});
|
||||
const toFlatViewFilterMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: fromFlatViewFilterMaps,
|
||||
entityToDeleteId: existingViewFilterToDelete.id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewFilterMaps: {
|
||||
from: fromFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: true,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while destroying view filter',
|
||||
);
|
||||
}
|
||||
|
||||
return existingViewFilterToDelete;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -168,7 +168,7 @@ export class ViewFilterService {
|
||||
return viewFilter;
|
||||
}
|
||||
|
||||
async destroy(id: string, workspaceId: string): Promise<boolean> {
|
||||
async destroy(id: string, workspaceId: string): Promise<ViewFilterEntity> {
|
||||
const viewFilter = await this.findById(id, workspaceId);
|
||||
|
||||
if (!isDefined(viewFilter)) {
|
||||
@@ -185,7 +185,7 @@ export class ViewFilterService {
|
||||
|
||||
await this.flushGraphQLCache(workspaceId);
|
||||
|
||||
return true;
|
||||
return viewFilter;
|
||||
}
|
||||
|
||||
private async flushGraphQLCache(workspaceId: string): Promise<void> {
|
||||
|
||||
+11
-3
@@ -1,19 +1,27 @@
|
||||
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 { ViewFilterController } from 'src/engine/metadata-modules/view-filter/controllers/view-filter.controller';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewFilterResolver } from 'src/engine/metadata-modules/view-filter/resolvers/view-filter.resolver';
|
||||
import { ViewFilterV2Service } from 'src/engine/metadata-modules/view-filter/services/view-filter-v2.service';
|
||||
import { ViewFilterService } from 'src/engine/metadata-modules/view-filter/services/view-filter.service';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ViewFilterEntity]),
|
||||
TypeOrmModule.forFeature([ViewFilterEntity, ViewEntity]),
|
||||
WorkspaceCacheStorageModule,
|
||||
FeatureFlagModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
controllers: [ViewFilterController],
|
||||
providers: [ViewFilterService, ViewFilterResolver],
|
||||
exports: [ViewFilterService],
|
||||
providers: [ViewFilterService, ViewFilterResolver, ViewFilterV2Service],
|
||||
exports: [ViewFilterService, ViewFilterV2Service],
|
||||
})
|
||||
export class ViewFilterModule {}
|
||||
|
||||
+5
@@ -7,10 +7,12 @@ import { WorkspaceFlatFieldMetadataMapCacheService } from 'src/engine/metadata-m
|
||||
import { WorkspaceFlatIndexMapCacheService } from 'src/engine/metadata-modules/flat-index-metadata/services/workspace-flat-index-map-cache.service';
|
||||
import { WorkspaceFlatObjectMetadataMapCacheService } from 'src/engine/metadata-modules/flat-object-metadata/services/workspace-flat-object-metadata-map-cache.service';
|
||||
import { WorkspaceFlatViewFieldMapCacheService } from 'src/engine/metadata-modules/flat-view-field/services/workspace-flat-view-field-map-cache.service';
|
||||
import { WorkspaceFlatViewFilterMapCacheService } from 'src/engine/metadata-modules/flat-view-filter/services/workspace-flat-view-filter-map-cache.service';
|
||||
import { WorkspaceFlatViewMapCacheService } from 'src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
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';
|
||||
@@ -26,6 +28,7 @@ import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-
|
||||
TypeOrmModule.forFeature([
|
||||
ViewEntity,
|
||||
ViewFieldEntity,
|
||||
ViewFilterEntity,
|
||||
IndexMetadataEntity,
|
||||
FieldMetadataEntity,
|
||||
ObjectMetadataEntity,
|
||||
@@ -36,6 +39,7 @@ import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-
|
||||
WorkspaceFlatObjectMetadataMapCacheService,
|
||||
WorkspaceFlatViewMapCacheService,
|
||||
WorkspaceFlatViewFieldMapCacheService,
|
||||
WorkspaceFlatViewFilterMapCacheService,
|
||||
WorkspaceFlatIndexMapCacheService,
|
||||
WorkspaceFlatFieldMetadataMapCacheService,
|
||||
],
|
||||
@@ -44,6 +48,7 @@ import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-
|
||||
WorkspaceFlatObjectMetadataMapCacheService,
|
||||
WorkspaceFlatViewMapCacheService,
|
||||
WorkspaceFlatViewFieldMapCacheService,
|
||||
WorkspaceFlatViewFilterMapCacheService,
|
||||
WorkspaceFlatIndexMapCacheService,
|
||||
WorkspaceFlatFieldMetadataMapCacheService,
|
||||
],
|
||||
|
||||
+1
@@ -19,4 +19,5 @@ export const EMPTY_ORCHESTRATOR_ACTIONS_REPORT = {
|
||||
databaseEventTrigger: emptyCreatedDeletedUpdated,
|
||||
cronTrigger: emptyCreatedDeletedUpdated,
|
||||
routeTrigger: emptyCreatedDeletedUpdated,
|
||||
viewFilter: emptyCreatedDeletedUpdated,
|
||||
} as const satisfies OrchestratorActionsReport;
|
||||
|
||||
+2
@@ -10,6 +10,7 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
|
||||
): ValidationErrorResponse => {
|
||||
const emptyResponseError: ValidationErrorResponse = {
|
||||
summary: {
|
||||
invalidViewFilter: 0,
|
||||
invalidObjectMetadata: 0,
|
||||
invalidView: 0,
|
||||
invalidViewField: 0,
|
||||
@@ -31,6 +32,7 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
|
||||
databaseEventTrigger: [],
|
||||
cronTrigger: [],
|
||||
routeTrigger: [],
|
||||
viewFilter: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+37
@@ -20,6 +20,7 @@ import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/work
|
||||
import { WorkspaceMigrationV2RouteTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/workspace-migration-v2-route-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuildOrchestratorService {
|
||||
@@ -28,6 +29,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
private readonly workspaceMigrationV2IndexActionsBuilderService: WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewFilterActionsBuilderService: WorkspaceMigrationV2ViewFilterActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ServerlessFunctionActionsBuilderService: WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
private readonly workspaceMigrationV2DatabaseEventTriggerActionsBuilderService: WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
private readonly workspaceMigrationV2CronTriggerActionsBuilderService: WorkspaceMigrationV2CronTriggerActionsBuilderService,
|
||||
@@ -88,6 +90,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
cronTrigger: [],
|
||||
routeTrigger: [],
|
||||
fieldMetadata: [],
|
||||
viewFilter: [],
|
||||
};
|
||||
|
||||
const optimisticAllFlatEntityMaps = this.setupOptimisticCache({
|
||||
@@ -104,6 +107,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
flatCronTriggerMaps,
|
||||
flatRouteTriggerMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatViewFilterMaps,
|
||||
} = fromToAllFlatEntityMaps;
|
||||
|
||||
if (isDefined(flatObjectMetadataMaps)) {
|
||||
@@ -268,6 +272,36 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatViewFilterMaps)) {
|
||||
const { from: fromFlatViewFilterMaps, to: toFlatViewFilterMaps } =
|
||||
flatViewFilterMaps;
|
||||
const viewFilterResult =
|
||||
await this.workspaceMigrationV2ViewFilterActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
from: fromFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatViewFilterMaps =
|
||||
viewFilterResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatViewMaps =
|
||||
viewFilterResult.dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
|
||||
if (viewFilterResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewFilter.push(...viewFilterResult.errors);
|
||||
} else {
|
||||
orchestratorActionsReport.viewFilter = viewFilterResult.actions;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatServerlessFunctionMaps)) {
|
||||
const {
|
||||
from: fromFlatServerlessFunctionMaps,
|
||||
@@ -434,6 +468,9 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
...aggregatedOrchestratorActionsReport.viewField.deleted,
|
||||
...aggregatedOrchestratorActionsReport.viewField.created,
|
||||
...aggregatedOrchestratorActionsReport.viewField.updated,
|
||||
...aggregatedOrchestratorActionsReport.viewFilter.deleted,
|
||||
...aggregatedOrchestratorActionsReport.viewFilter.created,
|
||||
...aggregatedOrchestratorActionsReport.viewFilter.updated,
|
||||
///
|
||||
|
||||
// Serverless functions
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
|
||||
export type ViewFilterRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatViewMaps' | 'flatFieldMetadataMaps'
|
||||
>;
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { compareTwoFlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/utils/compare-two-flat-view-filter.util';
|
||||
import { ViewFilterRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/types/view-filter-related-flat-entity-maps.type';
|
||||
import {
|
||||
FlatEntityUpdateValidationArgs,
|
||||
FlatEntityValidationArgs,
|
||||
FlatEntityValidationReturnType,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import {
|
||||
UpdateViewFilterAction,
|
||||
WorkspaceMigrationViewFilterActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-filter-action-v2.type';
|
||||
import { FlatViewFilterValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewFilterActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'viewFilter',
|
||||
FlatViewFilter,
|
||||
WorkspaceMigrationViewFilterActionV2,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatViewFilterValidatorService: FlatViewFilterValidatorService,
|
||||
) {
|
||||
super('viewFilter');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatViewFilterToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFilterActionV2,
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatViewFilterValidatorService.validateFlatViewFilterCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatViewFilterToValidate,
|
||||
optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFilterToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
viewFilterIds: [...flatView.viewFilterIds, flatViewFilterToValidate.id],
|
||||
},
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create_view_filter',
|
||||
viewFilter: flatViewFilterToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatViewFilterToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFilterActionV2,
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatViewFilterValidatorService.validateFlatViewFilterDeletion({
|
||||
flatViewFilterToValidate,
|
||||
optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewMaps = isDefined(flatView)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
viewFilterIds: flatView.viewFilterIds.filter(
|
||||
(id) => id !== flatViewFilterToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_view_filter',
|
||||
viewFilterId: flatViewFilterToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: { from: fromFlatViewFilter, to: toFlatViewFilter },
|
||||
optimisticFlatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFilterActionV2,
|
||||
FlatViewFilter,
|
||||
ViewFilterRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
const viewFilterUpdatedProperties = compareTwoFlatViewFilter({
|
||||
fromFlatViewFilter,
|
||||
toFlatViewFilter,
|
||||
});
|
||||
|
||||
if (viewFilterUpdatedProperties.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const validationResult =
|
||||
this.flatViewFilterValidatorService.validateFlatViewFilterUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatViewFilterToValidate: toFlatViewFilter,
|
||||
optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const updateViewFilterAction: UpdateViewFilterAction = {
|
||||
type: 'update_view_filter',
|
||||
viewFilterId: toFlatViewFilter.id,
|
||||
updates: viewFilterUpdatedProperties,
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateViewFilterAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
@@ -7,6 +7,7 @@ import { type WorkspaceMigrationRouteTriggerActionV2 } from 'src/engine/workspac
|
||||
import { type WorkspaceMigrationServerlessFunctionActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-serverless-function-action-v2.type';
|
||||
import { type WorkspaceMigrationViewActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-action-v2.type';
|
||||
import { type WorkspaceMigrationViewFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-field-action-v2.type';
|
||||
import { type WorkspaceMigrationViewFilterActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-filter-action-v2.type';
|
||||
|
||||
export type WorkspaceMigrationActionV2 =
|
||||
| WorkspaceMigrationObjectActionV2
|
||||
@@ -14,6 +15,7 @@ export type WorkspaceMigrationActionV2 =
|
||||
| WorkspaceMigrationIndexActionV2
|
||||
| WorkspaceMigrationViewActionV2
|
||||
| WorkspaceMigrationViewFieldActionV2
|
||||
| WorkspaceMigrationViewFilterActionV2
|
||||
| WorkspaceMigrationServerlessFunctionActionV2
|
||||
| WorkspaceMigrationDatabaseEventTriggerActionV2
|
||||
| WorkspaceMigrationCronTriggerActionV2
|
||||
|
||||
+6
-16
@@ -1,30 +1,20 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatViewFieldPropertiesToCompare } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field-properties-to-compare.type';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { type ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
|
||||
export type FlatViewFieldPropertyUpdate<
|
||||
P extends FlatViewFieldPropertiesToCompare,
|
||||
> = {
|
||||
property: P;
|
||||
} & FromTo<ViewFieldEntity[P]>;
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
|
||||
export type CreateViewFieldAction = {
|
||||
type: 'create_view_field';
|
||||
viewField: FlatViewField;
|
||||
};
|
||||
|
||||
export type FlatViewFieldPropertiesUpdates = Array<
|
||||
{
|
||||
[P in FlatViewFieldPropertiesToCompare]: FlatViewFieldPropertyUpdate<P>;
|
||||
}[FlatViewFieldPropertiesToCompare]
|
||||
>;
|
||||
|
||||
export type UpdateViewFieldAction = {
|
||||
type: 'update_view_field';
|
||||
viewFieldId: string;
|
||||
updates: FlatViewFieldPropertiesUpdates;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatViewFieldPropertiesToCompare]: PropertyUpdate<FlatViewField, P>;
|
||||
}[FlatViewFieldPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
export type DeleteViewFieldAction = {
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { type FlatViewFilterPropertiesToCompare } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-properties-to-compare.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
|
||||
export type CreateViewFilterAction = {
|
||||
type: 'create_view_filter';
|
||||
viewFilter: FlatViewFilter;
|
||||
};
|
||||
|
||||
export type UpdateViewFilterAction = {
|
||||
type: 'update_view_filter';
|
||||
viewFilterId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatViewFilterPropertiesToCompare]: PropertyUpdate<
|
||||
FlatViewFilter,
|
||||
P
|
||||
>;
|
||||
}[FlatViewFilterPropertiesToCompare]
|
||||
>;
|
||||
};
|
||||
|
||||
export type DeleteViewFilterAction = {
|
||||
type: 'delete_view_filter';
|
||||
viewFilterId: string;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationViewFilterActionV2 =
|
||||
| CreateViewFilterAction
|
||||
| UpdateViewFilterAction
|
||||
| DeleteViewFilterAction;
|
||||
|
||||
export type WorkspaceMigrationViewFilterActionTypeV2 =
|
||||
WorkspaceMigrationViewFilterActionV2['type'];
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatViewFilterMaps } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter-maps.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { ViewFilterExceptionCode } from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { ViewFilterRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/types/view-filter-related-flat-entity-maps.type';
|
||||
|
||||
@Injectable()
|
||||
export class FlatViewFilterValidatorService {
|
||||
constructor() {}
|
||||
|
||||
validateFlatViewFilterCreation({
|
||||
flatViewFilterToValidate,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
optimisticFlatViewFilterMaps,
|
||||
}: {
|
||||
flatViewFilterToValidate: FlatViewFilter;
|
||||
dependencyOptimisticFlatEntityMaps: ViewFilterRelatedFlatEntityMaps;
|
||||
optimisticFlatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FailedFlatEntityValidation<FlatViewFilter> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewFilter> = {
|
||||
type: 'create_view_filter',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewFilterToValidate.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingViewFilter = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.id,
|
||||
flatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (isDefined(existingViewFilter)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`View filter with this id already exists`,
|
||||
userFriendlyMessage: msg`View filter with this id already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
const referencedView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedView)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: msg`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const referencedFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`Field metadata not found`,
|
||||
userFriendlyMessage: msg`Field metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validateFlatViewFilterDeletion({
|
||||
flatViewFilterToValidate,
|
||||
optimisticFlatViewFilterMaps,
|
||||
}: {
|
||||
flatViewFilterToValidate: FlatViewFilter;
|
||||
optimisticFlatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FailedFlatEntityValidation<FlatViewFilter> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewFilter> = {
|
||||
type: 'delete_view_filter',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewFilterToValidate.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingViewFilter = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.id,
|
||||
flatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingViewFilter)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.VIEW_FILTER_NOT_FOUND,
|
||||
message: t`View filter not found`,
|
||||
userFriendlyMessage: msg`View filter not found`,
|
||||
});
|
||||
} else if (!isDefined(existingViewFilter.deletedAt)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`View filter has to be soft deleted first`,
|
||||
userFriendlyMessage: msg`View filter has to be soft deleted first`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validateFlatViewFilterUpdate({
|
||||
flatViewFilterToValidate,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
optimisticFlatViewFilterMaps,
|
||||
}: {
|
||||
flatViewFilterToValidate: FlatViewFilter;
|
||||
dependencyOptimisticFlatEntityMaps: ViewFilterRelatedFlatEntityMaps;
|
||||
optimisticFlatViewFilterMaps: FlatViewFilterMaps;
|
||||
}): FailedFlatEntityValidation<FlatViewFilter> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewFilter> = {
|
||||
type: 'update_view_filter',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewFilterToValidate.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingViewFilter = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.id,
|
||||
flatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingViewFilter)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.VIEW_FILTER_NOT_FOUND,
|
||||
message: t`View filter not found`,
|
||||
userFriendlyMessage: msg`View filter not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const referencedFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFilterToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`Field metadata not found`,
|
||||
userFriendlyMessage: msg`Field metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
+3
@@ -10,6 +10,7 @@ import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager
|
||||
import { FlatRouteTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-route-trigger-validator.service';
|
||||
import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
|
||||
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
|
||||
import { FlatViewFilterValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service';
|
||||
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
|
||||
|
||||
@Module({
|
||||
@@ -17,6 +18,7 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace
|
||||
providers: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatViewFilterValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
@@ -29,6 +31,7 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace
|
||||
exports: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatViewFilterValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
|
||||
+3
@@ -10,6 +10,7 @@ import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/work
|
||||
import { WorkspaceMigrationV2RouteTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/workspace-migration-v2-route-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module';
|
||||
|
||||
@@ -20,6 +21,7 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-
|
||||
WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFilterActionsBuilderService,
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
@@ -32,6 +34,7 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFilterActionsBuilderService,
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
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 { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { CreateViewFilterAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-filter-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewFilterActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'create_view_filter',
|
||||
) {
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateViewFilterAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFilterMaps } = allFlatEntityMaps;
|
||||
const { viewFilter } = action;
|
||||
|
||||
const updatedFlatViewFilterMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: viewFilter,
|
||||
flatEntityMaps: flatViewFilterMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewFilterMaps: updatedFlatViewFilterMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateViewFilterAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { viewFilter } = action;
|
||||
|
||||
const viewFilterRepository =
|
||||
queryRunner.manager.getRepository<ViewFilterEntity>(ViewFilterEntity);
|
||||
|
||||
await viewFilterRepository.insert({
|
||||
...viewFilter,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<CreateViewFilterAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
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 { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { DeleteViewFilterAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-filter-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewFilterActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'delete_view_filter',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteViewFilterAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFilterMaps } = allFlatEntityMaps;
|
||||
const { viewFilterId } = action;
|
||||
|
||||
const updatedFlatViewFilterMaps = deleteFlatEntityFromFlatEntityMapsOrThrow(
|
||||
{
|
||||
entityToDeleteId: viewFilterId,
|
||||
flatEntityMaps: flatViewFilterMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
flatViewFilterMaps: updatedFlatViewFilterMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<DeleteViewFilterAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { viewFilterId } = action;
|
||||
|
||||
const viewFilterRepository =
|
||||
queryRunner.manager.getRepository<ViewFilterEntity>(ViewFilterEntity);
|
||||
|
||||
await viewFilterRepository.delete({
|
||||
id: viewFilterId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<DeleteViewFilterAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
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 { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { UpdateViewFilterAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-view-filter-action-v2.type';
|
||||
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
import { fromWorkspaceMigrationUpdateActionToPartialEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-workspace-migration-update-action-to-partial-field-or-object-entity.util';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewFilterActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
|
||||
'update_view_filter',
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateViewFilterAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatViewFilterMaps } = allFlatEntityMaps;
|
||||
const { viewFilterId } = action;
|
||||
|
||||
const existingViewFilter = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: viewFilterId,
|
||||
flatEntityMaps: flatViewFilterMaps,
|
||||
});
|
||||
|
||||
const updatedViewFilter = {
|
||||
...existingViewFilter,
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatViewFilterMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: updatedViewFilter,
|
||||
flatEntityMaps: flatViewFilterMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewFilterMaps: updatedFlatViewFilterMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async executeForMetadata(
|
||||
context: WorkspaceMigrationActionRunnerArgs<UpdateViewFilterAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner } = context;
|
||||
const { viewFilterId } = action;
|
||||
|
||||
const viewFilterRepository =
|
||||
queryRunner.manager.getRepository<ViewFilterEntity>(ViewFilterEntity);
|
||||
|
||||
const update = fromWorkspaceMigrationUpdateActionToPartialEntity(action);
|
||||
|
||||
await viewFilterRepository.update(viewFilterId, update);
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerArgs<UpdateViewFilterAction>,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+7
@@ -24,6 +24,9 @@ import { UpdateServerlessFunctionActionHandlerService } from 'src/engine/workspa
|
||||
import { CreateViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/create-view-field-action-handler.service';
|
||||
import { DeleteViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/delete-view-field-action-handler.service';
|
||||
import { UpdateViewFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-field/services/update-view-field-action-handler.service';
|
||||
import { CreateViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/create-view-filter-action-handler.service';
|
||||
import { DeleteViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/delete-view-filter-action-handler.service';
|
||||
import { UpdateViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/update-view-filter-action-handler.service';
|
||||
import { CreateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/create-view-action-handler.service';
|
||||
import { DeleteViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/delete-view-action-handler.service';
|
||||
import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view/services/update-view-action-handler.service';
|
||||
@@ -50,6 +53,10 @@ import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/wor
|
||||
UpdateViewFieldActionHandlerService,
|
||||
DeleteViewFieldActionHandlerService,
|
||||
|
||||
CreateViewFilterActionHandlerService,
|
||||
UpdateViewFilterActionHandlerService,
|
||||
DeleteViewFilterActionHandlerService,
|
||||
|
||||
CreateServerlessFunctionActionHandlerService,
|
||||
DeleteServerlessFunctionActionHandlerService,
|
||||
UpdateServerlessFunctionActionHandlerService,
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function orderObjectProperties<T extends Array<any> | object>(
|
||||
data: T,
|
||||
): T {
|
||||
export function orderObjectProperties<T>(data: T): T {
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(orderObjectProperties) as T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user