[WHEN_RELEASED_REQUIRES_CACHE_FLUSH] Object related record logic in v2 (#14937)
# Introduction
Initial motivation here was to migrate the object related records logic
from v1 to v2, please note that now in v2 views aren't records anymore
but core engine entities
## What's done
- Added specific label identifier targeting view field logic
- Handled side effects on viewField creation with lowest position on
object label identifier mutation
- Added viewField relations in field metadate entity + handled
optimistic in builder v2
- Added view relations in object metadata entity + handled optimistic in
builder v2
- Added integration tests covering the side effects and new validation
exceptions
- Sandardized cache computation
- Coverage on object metadata creation side effect on views and view
fields
## Coverage
```ts
PASS test/integration/graphql/suites/view/view-field/object-identifier-update-side-effect-on-view-field.integration-spec.ts
View Field Resolver - Successful object metadata identifier update side effect on view field
✓ should create a view field on label identifier object metadata update if it does not exist on view (7 ms)
✓ Should not allow deleting a label identifier view field (17 ms)
✓ Should not allow destroying a label identifier view field (6 ms)
✓ Should not allow updating a label identifier view field visibility to false (8 ms)
✓ Should not allow creating a view field with a position lower than the label idenfitier view field (180 ms)
✓ Should not allow updated labelIdentifier view field with a position higher than existing other view field (346 ms)
✓ Should allow updated labelIdentifier view field with a position higher than existing other view field (434 ms)
Test Suites: 1 passed, 1 total
Tests: 7 passed, 7 total
Snapshots: 5 passed, 5 total
Time: 4.571 s, estimated 5 s
```
close https://github.com/twentyhq/core-team-issues/issues/1664
This commit is contained in:
+25
@@ -0,0 +1,25 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
import { getSubFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/get-sub-flat-entity-maps-or-throw.util';
|
||||
|
||||
export type FindManyFlatEntityByIdInFlatEntityMapsOrThrowArgs<
|
||||
T extends FlatEntity,
|
||||
> = {
|
||||
flatEntityMaps: FlatEntityMaps<T>;
|
||||
flatEntityIds: string[];
|
||||
};
|
||||
export const findManyFlatEntityByIdInFlatEntityMapsOrThrow = <
|
||||
T extends FlatEntity,
|
||||
>({
|
||||
flatEntityMaps,
|
||||
flatEntityIds,
|
||||
}: FindManyFlatEntityByIdInFlatEntityMapsOrThrowArgs<T>): T[] => {
|
||||
const subFlatEntityMaps = getSubFlatEntityMapsOrThrow<T>({
|
||||
flatEntityIds,
|
||||
flatEntityMaps,
|
||||
});
|
||||
|
||||
return Object.values(subFlatEntityMaps.byId).filter(isDefined);
|
||||
};
|
||||
+8
-13
@@ -6,6 +6,8 @@ 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 { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { fromViewFieldEntityToFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/from-view-field-entity-to-flat-view-field.util';
|
||||
@@ -36,20 +38,13 @@ export class WorkspaceFlatViewFieldMapCacheService extends WorkspaceFlatMapCache
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
const flatViewFieldMaps: FlatViewFieldMaps = {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
for (const viewFieldEntity of existingViewFields) {
|
||||
return existingViewFields.reduce((flatViewFieldMaps, viewFieldEntity) => {
|
||||
const flatViewField = fromViewFieldEntityToFlatViewField(viewFieldEntity);
|
||||
|
||||
flatViewFieldMaps.byId[flatViewField.id] = flatViewField;
|
||||
flatViewFieldMaps.idByUniversalIdentifier[
|
||||
flatViewField.universalIdentifier
|
||||
] = flatViewField.id;
|
||||
}
|
||||
|
||||
return flatViewFieldMaps;
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewField,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -6,9 +6,11 @@ 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 { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { generateFlatViewMaps } from 'src/engine/core-modules/view/flat-view/utils/generate-flat-view-maps.util';
|
||||
import { fromViewEntityToFlatView } from 'src/engine/core-modules/view/flat-view/utils/from-view-entity-to-flat-view.util';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@@ -33,9 +35,22 @@ export class WorkspaceFlatViewMapCacheService extends WorkspaceFlatMapCacheServi
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
select: {
|
||||
viewFields: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
relations: ['viewFields'],
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
return generateFlatViewMaps(views);
|
||||
return views.reduce((flatViewMaps, viewEntity) => {
|
||||
const flatView = fromViewEntityToFlatView(viewEntity);
|
||||
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatView,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
|
||||
export const fromCreateViewFieldInputToFlatViewFieldToCreate = ({
|
||||
createViewFieldInput: rawCreateViewFieldInput,
|
||||
@@ -30,7 +31,7 @@ export const fromCreateViewFieldInputToFlatViewFieldToCreate = ({
|
||||
deletedAt: null,
|
||||
universalIdentifier: viewFieldId,
|
||||
isVisible: createViewFieldInput.isVisible ?? true,
|
||||
size: createViewFieldInput.size ?? 0,
|
||||
size: createViewFieldInput.size ?? DEFAULT_VIEW_FIELD_SIZE,
|
||||
position: createViewFieldInput.position ?? 0,
|
||||
aggregateOperation: createViewFieldInput.aggregateOperation ?? null,
|
||||
};
|
||||
|
||||
+1
-1
@@ -15,6 +15,6 @@ 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) ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type FlatViewMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-maps.type';
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
export const generateFlatViewMaps = (
|
||||
viewCollection: ViewEntity[],
|
||||
): FlatViewMaps => {
|
||||
const flatViewMaps: FlatViewMaps = {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
for (const view of viewCollection) {
|
||||
const processedFlatView: FlatView = {
|
||||
...view,
|
||||
viewFieldIds: view.viewFields?.map((viewField) => viewField.id) ?? [],
|
||||
universalIdentifier: view.universalIdentifier ?? view.id, // TODO: should not fallback to id, to remove once we have universalIdentifier required
|
||||
};
|
||||
|
||||
flatViewMaps.byId[view.id] = processedFlatView;
|
||||
flatViewMaps.idByUniversalIdentifier[
|
||||
processedFlatView.universalIdentifier
|
||||
] = view.id;
|
||||
}
|
||||
|
||||
return flatViewMaps;
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
export const isViewFieldInLowestPosition = ({
|
||||
flatViewField,
|
||||
otherFlatViewFields,
|
||||
}: {
|
||||
otherFlatViewFields: FlatViewField[];
|
||||
flatViewField: FlatViewField;
|
||||
}) => {
|
||||
if (otherFlatViewFields.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const positions = otherFlatViewFields.map(({ position }) => position);
|
||||
const lowestPosition = Math.min(...positions);
|
||||
|
||||
return flatViewField.position < lowestPosition;
|
||||
};
|
||||
+43
-21
@@ -2,6 +2,7 @@ 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';
|
||||
@@ -38,6 +39,7 @@ export class ViewFieldV2Service {
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -45,6 +47,7 @@ export class ViewFieldV2Service {
|
||||
'flatViewFieldMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatObjectMetadataMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -72,6 +75,7 @@ export class ViewFieldV2Service {
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatFieldMetadataMaps,
|
||||
flatViewMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
@@ -109,13 +113,20 @@ export class ViewFieldV2Service {
|
||||
workspaceId: string;
|
||||
updateViewFieldInput: UpdateViewFieldInput;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
const {
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFieldMaps',
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatView =
|
||||
fromUpdateViewFieldInputToFlatViewFieldToUpdateOrThrow({
|
||||
@@ -123,13 +134,9 @@ export class ViewFieldV2Service {
|
||||
updateViewFieldInput,
|
||||
});
|
||||
|
||||
const fromFlatViewFieldMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: [optimisticallyUpdatedFlatView.id],
|
||||
flatEntityMaps: existingFlatViewFieldMaps,
|
||||
});
|
||||
const toFlatViewFieldMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
const toFlatViewFieldMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: optimisticallyUpdatedFlatView,
|
||||
flatEntityMaps: fromFlatViewFieldMaps,
|
||||
flatEntityMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
@@ -137,10 +144,14 @@ export class ViewFieldV2Service {
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewFieldMaps: {
|
||||
from: fromFlatViewFieldMaps,
|
||||
from: existingFlatViewFieldMaps,
|
||||
to: toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
@@ -177,13 +188,20 @@ export class ViewFieldV2Service {
|
||||
deleteViewFieldInput: DeleteViewFieldInput;
|
||||
workspaceId: string;
|
||||
}): Promise<ViewFieldDTO> {
|
||||
const { flatViewFieldMaps: existingFlatViewFieldMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
const {
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewFieldMaps',
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatViewWithDeletedAt =
|
||||
fromDeleteViewFieldInputToFlatViewFieldOrThrow({
|
||||
@@ -205,6 +223,10 @@ export class ViewFieldV2Service {
|
||||
to: toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: false,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { type IndexMetadataInterface } from 'src/engine/metadata-modules/index-m
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||
@@ -18,7 +19,6 @@ import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/rel
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
||||
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
|
||||
import { fromMorphOrRelationFlatFieldMetadataToRelationDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-or-relation-flat-field-metadata-to-relation-dto.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
@@ -361,10 +361,10 @@ export class DataloaderService {
|
||||
flatEntityId: objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
|
||||
const overriddenFieldMetadataEntities =
|
||||
|
||||
+8
-7
@@ -19,6 +19,7 @@ import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-met
|
||||
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { type FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
|
||||
import { AssignIfIsGivenFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-if-is-given-field-metadata-type.type';
|
||||
import { AssignTypeIfIsMorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-type-if-is-morph-or-relation-field-metadata-type.type';
|
||||
@@ -133,8 +134,7 @@ export class FieldMetadataEntity<
|
||||
|
||||
@OneToOne(
|
||||
() => FieldMetadataEntity,
|
||||
(fieldMetadata: FieldMetadataEntity) =>
|
||||
fieldMetadata.relationTargetFieldMetadataId,
|
||||
(fieldMetadata) => fieldMetadata.relationTargetFieldMetadataId,
|
||||
{ nullable: true },
|
||||
)
|
||||
@JoinColumn({ name: 'relationTargetFieldMetadataId' })
|
||||
@@ -151,8 +151,7 @@ export class FieldMetadataEntity<
|
||||
|
||||
@ManyToOne(
|
||||
() => ObjectMetadataEntity,
|
||||
(objectMetadata: ObjectMetadataEntity) =>
|
||||
objectMetadata.targetRelationFields,
|
||||
(objectMetadata) => objectMetadata.targetRelationFields,
|
||||
{ onDelete: 'CASCADE', nullable: true },
|
||||
)
|
||||
@JoinColumn({ name: 'relationTargetObjectMetadataId' })
|
||||
@@ -170,8 +169,7 @@ export class FieldMetadataEntity<
|
||||
|
||||
@OneToMany(
|
||||
() => IndexFieldMetadataEntity,
|
||||
(indexFieldMetadata: IndexFieldMetadataEntity) =>
|
||||
indexFieldMetadata.indexMetadata,
|
||||
(indexFieldMetadata) => indexFieldMetadata.indexMetadata,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
@@ -186,7 +184,10 @@ export class FieldMetadataEntity<
|
||||
|
||||
@OneToMany(
|
||||
() => FieldPermissionEntity,
|
||||
(fieldPermission: FieldPermissionEntity) => fieldPermission.fieldMetadata,
|
||||
(fieldPermission) => fieldPermission.fieldMetadata,
|
||||
)
|
||||
fieldPermissions: Relation<FieldPermissionEntity[]>;
|
||||
|
||||
@OneToMany(() => ViewFieldEntity, (viewField) => viewField.fieldMetadata)
|
||||
viewFields: Relation<ViewFieldEntity[]>;
|
||||
}
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
viewFieldIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
defaultValue: null,
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ export const getRelationTargetFlatFieldMetadataMock = ({
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
viewFieldIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
description: 'default flat field metadata description',
|
||||
|
||||
+4
@@ -113,6 +113,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -145,6 +146,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -175,6 +177,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -207,6 +210,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
],
|
||||
|
||||
+18
-19
@@ -8,6 +8,7 @@ import { CacheStorageService } from 'src/engine/core-modules/cache-storage/servi
|
||||
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 { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { fromFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util';
|
||||
@@ -37,28 +38,26 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceFlatMapC
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
select: {
|
||||
viewFields: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: [],
|
||||
relations: ['viewFields'],
|
||||
});
|
||||
|
||||
const flatFieldMetadataMaps = fieldMetadatas.reduce<
|
||||
FlatEntityMaps<FlatFieldMetadata>
|
||||
>((flatEntityMaps, field) => {
|
||||
const flatFieldMetadata =
|
||||
fromFieldMetadataEntityToFlatFieldMetadata(field);
|
||||
return fieldMetadatas.reduce(
|
||||
(flatFieldMetadataMaps, fieldMetadataEntity) => {
|
||||
const flatFieldMetadata =
|
||||
fromFieldMetadataEntityToFlatFieldMetadata(fieldMetadataEntity);
|
||||
|
||||
return {
|
||||
byId: {
|
||||
...flatEntityMaps.byId,
|
||||
[flatFieldMetadata.id]: flatFieldMetadata,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
...flatEntityMaps.idByUniversalIdentifier,
|
||||
[flatFieldMetadata.universalIdentifier]: flatFieldMetadata.id,
|
||||
},
|
||||
};
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
|
||||
return flatFieldMetadataMaps;
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatFieldMetadata,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
},
|
||||
EMPTY_FLAT_ENTITY_MAPS,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ export const fieldMetadataRelationProperties = [
|
||||
'fieldPermissions',
|
||||
'indexFieldMetadatas',
|
||||
'object',
|
||||
'viewFields',
|
||||
] as const satisfies (keyof FieldMetadataEntity)[];
|
||||
|
||||
export type FieldMetadataEntityRelationProperties =
|
||||
@@ -16,5 +17,5 @@ export type FieldMetadataEntityRelationProperties =
|
||||
export type FlatFieldMetadata<T extends FieldMetadataType = FieldMetadataType> =
|
||||
Omit<FieldMetadataEntity<T>, FieldMetadataEntityRelationProperties> & {
|
||||
universalIdentifier: string;
|
||||
// Could add indexField metadatas and so on ?
|
||||
viewFieldIds: string[];
|
||||
};
|
||||
|
||||
+6
-5
@@ -6,8 +6,8 @@ import {
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@@ -30,10 +30,11 @@ export const findAllOthersMorphRelationFlatFieldMetadatasOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
|
||||
return objectFlatFieldMetadatas.filter(
|
||||
(
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { getSubFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/get-sub-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
type FindObjectFieldsInFlatFieldMetadataMapsArgs = {
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
};
|
||||
export const findObjectFlatFieldMetadatasOrThrow = ({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
}: FindObjectFieldsInFlatFieldMetadataMapsArgs): {
|
||||
objectFlatFieldMetadatas: FlatFieldMetadata[];
|
||||
} => {
|
||||
const objectFlatFieldMetadataMaps = getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const objectFlatFieldMetadatas = Object.values(
|
||||
objectFlatFieldMetadataMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
return {
|
||||
objectFlatFieldMetadatas,
|
||||
};
|
||||
};
|
||||
+6
-5
@@ -6,6 +6,7 @@ import {
|
||||
import { type 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 { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-flat-field-metadata.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
|
||||
@@ -140,10 +140,11 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
flatEntityMaps: existingFlatObjectMetadataMaps,
|
||||
flatEntityId: flatIndex.objectMetadataId,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: existingFlatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatObjectMetadata,
|
||||
|
||||
+3
@@ -20,6 +20,9 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = <
|
||||
|
||||
return {
|
||||
...fieldMetadataWithoutRelations,
|
||||
viewFieldIds: fieldMetadataEntity.viewFields.map(
|
||||
(viewField) => viewField.id,
|
||||
),
|
||||
universalIdentifier:
|
||||
fieldMetadataWithoutRelations.standardId ??
|
||||
fieldMetadataWithoutRelations.id,
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
const createdAt = new Date();
|
||||
|
||||
return {
|
||||
viewFieldIds: [],
|
||||
description: createFieldInput.description ?? null,
|
||||
id: fieldMetadataId,
|
||||
icon: createFieldInput.icon ?? null,
|
||||
|
||||
+6
-5
@@ -1,8 +1,8 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
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 { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
@@ -35,10 +35,11 @@ export const recomputeIndexOnFlatFieldMetadataNameUpdate = ({
|
||||
return [];
|
||||
}
|
||||
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
const optimisticObjectFlatFieldMetadatas = objectFlatFieldMetadatas.map(
|
||||
(flatFieldMetadata) => {
|
||||
if (flatFieldMetadata.id === fromFlatFieldMetadata.id) {
|
||||
|
||||
+6
-5
@@ -4,13 +4,13 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/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 { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
@@ -50,10 +50,11 @@ export const validateFlatFieldMetadataNameAvailability = ({
|
||||
remainingFlatEntityMapsToValidate?: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): FlatFieldMetadataValidationError[] => {
|
||||
const errors: FlatFieldMetadataValidationError[] = [];
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
const reservedCompositeFieldsNames = getReservedCompositeFieldNames(
|
||||
objectFlatFieldMetadatas,
|
||||
);
|
||||
|
||||
+12
-18
@@ -8,6 +8,7 @@ import { CacheStorageService } from 'src/engine/core-modules/cache-storage/servi
|
||||
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 { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { fromIndexMetadataEntityToFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
@@ -38,27 +39,20 @@ export class WorkspaceFlatIndexMapCacheService extends WorkspaceFlatMapCacheServ
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
select: {
|
||||
// Note: We need all IndexFieldMetadataEntity in order to build a FlatIndex
|
||||
indexFieldMetadatas: true,
|
||||
},
|
||||
relations: ['indexFieldMetadatas'],
|
||||
});
|
||||
|
||||
const flatIndexMaps = indexes.reduce<FlatEntityMaps<FlatIndexMetadata>>(
|
||||
(flatEntityMaps, index) => {
|
||||
const flatIndex = fromIndexMetadataEntityToFlatIndexMetadata(index);
|
||||
return indexes.reduce((flatIndexMaps, indexEntity) => {
|
||||
const flatIndex = fromIndexMetadataEntityToFlatIndexMetadata(indexEntity);
|
||||
|
||||
return {
|
||||
byId: {
|
||||
...flatEntityMaps.byId,
|
||||
[flatIndex.id]: flatIndex,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
...flatEntityMaps.idByUniversalIdentifier,
|
||||
[flatIndex.universalIdentifier]: flatIndex.id,
|
||||
},
|
||||
};
|
||||
},
|
||||
EMPTY_FLAT_ENTITY_MAPS,
|
||||
);
|
||||
|
||||
return flatIndexMaps;
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatIndex,
|
||||
flatEntityMaps: flatIndexMaps,
|
||||
});
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ export const getFlatObjectMetadataMock = (
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
viewIds: [],
|
||||
indexMetadataIds: [],
|
||||
fieldMetadataIds: [],
|
||||
description: 'default flat object metadata description',
|
||||
|
||||
+12
-1
@@ -39,7 +39,18 @@ export class WorkspaceFlatObjectMetadataMapCacheService extends WorkspaceFlatMap
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: ['fields', 'indexMetadatas'],
|
||||
select: {
|
||||
fields: {
|
||||
id: true,
|
||||
},
|
||||
indexMetadatas: {
|
||||
id: true,
|
||||
},
|
||||
views: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
relations: ['fields', 'indexMetadatas', 'views'],
|
||||
});
|
||||
|
||||
const flatObjectMetadataMaps = objectMetadatas.reduce<
|
||||
|
||||
+2
@@ -10,6 +10,7 @@ export const objectMetadataEntityRelationProperties = [
|
||||
'application',
|
||||
'objectPermissions',
|
||||
'fieldPermissions',
|
||||
'views',
|
||||
] as const satisfies ObjectMetadataRelationProperties[];
|
||||
|
||||
type ObjectMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||
@@ -24,4 +25,5 @@ export type FlatObjectMetadata = Omit<
|
||||
universalIdentifier: string;
|
||||
fieldMetadataIds: string[];
|
||||
indexMetadataIds: string[];
|
||||
viewIds: string[];
|
||||
};
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
const createdAt = new Date();
|
||||
const flatObjectMetadataToCreate: FlatObjectMetadata = {
|
||||
fieldMetadataIds: [],
|
||||
viewIds: [],
|
||||
indexMetadataIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
|
||||
+6
-5
@@ -5,8 +5,8 @@ import {
|
||||
|
||||
import { type 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 { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -52,10 +52,11 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToDelete,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadataToDelete.fieldMetadataIds,
|
||||
});
|
||||
const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap(
|
||||
(flatFieldMetadata) => {
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = (
|
||||
|
||||
return {
|
||||
...objectMetadataEntityWithoutRelations,
|
||||
viewIds: objectMetadataEntity.views.map((viewEntity) => viewEntity.id),
|
||||
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(
|
||||
(indexEntity) => indexEntity.id,
|
||||
),
|
||||
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { recomputeIndexAfterFlatObjectMetadataSingularNameUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-index-after-flat-object-metadata-singular-name-update.util';
|
||||
import { recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-view-field-identifier-after-flat-object-identifier-update.util';
|
||||
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
|
||||
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
|
||||
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
|
||||
updateObjectInput: UpdateOneObjectInput;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
| 'flatIndexMaps'
|
||||
| 'flatObjectMetadataMaps'
|
||||
| 'flatFieldMetadataMaps'
|
||||
| 'flatViewFieldMaps'
|
||||
| 'flatViewMaps'
|
||||
>;
|
||||
|
||||
const objectMetadataEditableProperties =
|
||||
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
|
||||
(
|
||||
property,
|
||||
): property is Exclude<
|
||||
FlatObjectMetadataPropertiesToCompare,
|
||||
'standardOverrides'
|
||||
> => property !== 'standardOverrides',
|
||||
);
|
||||
|
||||
type UpdatedFlatObjectAndRelatedFlatEntities = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
otherObjectFlatFieldMetadataToUpdate: FlatFieldMetadata[];
|
||||
flatIndexMetadataToUpdate: FlatIndexMetadata[];
|
||||
flatViewFieldToUpdate: FlatViewField[];
|
||||
flatViewFieldToCreate: FlatViewField[];
|
||||
};
|
||||
|
||||
export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
updateObjectInput: rawUpdateObjectInput,
|
||||
flatIndexMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndRelatedFlatEntities => {
|
||||
const { id: objectMetadataIdToUpdate } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateObjectInput,
|
||||
['id'],
|
||||
);
|
||||
const updatedEditableObjectProperties =
|
||||
extractAndSanitizeObjectStringFields(
|
||||
rawUpdateObjectInput.update,
|
||||
objectMetadataEditableProperties,
|
||||
);
|
||||
|
||||
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityMaps: existingFlatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataIdToUpdate,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadataToUpdate)) {
|
||||
throw new ObjectMetadataException(
|
||||
'Object to update not found',
|
||||
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (isStandardMetadata(flatObjectMetadataToUpdate)) {
|
||||
const invalidUpdatedProperties = Object.keys(
|
||||
updatedEditableObjectProperties,
|
||||
).filter(
|
||||
(property) =>
|
||||
!OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.includes(
|
||||
property as ObjectMetadataStandardOverridesProperties,
|
||||
),
|
||||
);
|
||||
|
||||
if (invalidUpdatedProperties.length > 0) {
|
||||
throw new ObjectMetadataException(
|
||||
`Cannot edit standard object metadata properties: ${invalidUpdatedProperties.join(', ')}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedStandardFlatObjectMetadata =
|
||||
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce(
|
||||
(acc, property) => {
|
||||
const isPropertyUpdated =
|
||||
updatedEditableObjectProperties[property] !== undefined;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
standardOverrides: {
|
||||
...acc.standardOverrides,
|
||||
...(isPropertyUpdated
|
||||
? { [property]: updatedEditableObjectProperties[property] }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
},
|
||||
flatObjectMetadataToUpdate,
|
||||
);
|
||||
|
||||
return {
|
||||
flatObjectMetadata: updatedStandardFlatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: [],
|
||||
flatIndexMetadataToUpdate: [],
|
||||
flatViewFieldToUpdate: [],
|
||||
flatViewFieldToCreate: [],
|
||||
};
|
||||
}
|
||||
|
||||
const initialAccumulator: UpdatedFlatObjectAndRelatedFlatEntities = {
|
||||
flatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
otherObjectFlatFieldMetadataToUpdate: [],
|
||||
flatIndexMetadataToUpdate: [],
|
||||
flatViewFieldToUpdate: [],
|
||||
flatViewFieldToCreate: [],
|
||||
};
|
||||
|
||||
return objectMetadataEditableProperties.reduce<UpdatedFlatObjectAndRelatedFlatEntities>(
|
||||
(
|
||||
{
|
||||
flatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate,
|
||||
flatIndexMetadataToUpdate,
|
||||
flatViewFieldToUpdate,
|
||||
flatViewFieldToCreate,
|
||||
},
|
||||
property,
|
||||
) => {
|
||||
const updatedPropertyValue = updatedEditableObjectProperties[property];
|
||||
const isPropertyUpdated =
|
||||
updatedPropertyValue !== undefined &&
|
||||
flatObjectMetadata[property] !== updatedPropertyValue;
|
||||
|
||||
if (!isPropertyUpdated) {
|
||||
return {
|
||||
flatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate,
|
||||
flatIndexMetadataToUpdate,
|
||||
flatViewFieldToUpdate,
|
||||
flatViewFieldToCreate,
|
||||
};
|
||||
}
|
||||
|
||||
const updatedFlatObjectMetadata = {
|
||||
...flatObjectMetadata,
|
||||
[property]: updatedPropertyValue,
|
||||
};
|
||||
|
||||
const newUpdatedOtherObjectFlatFieldMetadatas =
|
||||
property === 'nameSingular' || property === 'namePlural'
|
||||
? renameRelatedMorphFieldOnObjectNamesUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
fromFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
toFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
})
|
||||
: [];
|
||||
|
||||
const newUpdatedFlatIndexMetadatas =
|
||||
property === 'nameSingular'
|
||||
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
flatIndexMaps,
|
||||
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
|
||||
})
|
||||
: [];
|
||||
|
||||
const {
|
||||
flatViewFieldToCreate: newFlatViewFieldToCreate,
|
||||
flatViewFieldToUpdate: newFlatViewFieldToUpdate,
|
||||
} =
|
||||
property === 'labelIdentifierFieldMetadataId' &&
|
||||
isDefined(updatedFlatObjectMetadata.labelIdentifierFieldMetadataId)
|
||||
? recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate({
|
||||
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
flatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
updatedLabelIdentifierFieldMetadataId:
|
||||
updatedFlatObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
})
|
||||
: {
|
||||
flatViewFieldToCreate: [],
|
||||
flatViewFieldToUpdate: [],
|
||||
};
|
||||
|
||||
return {
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: [
|
||||
...otherObjectFlatFieldMetadataToUpdate,
|
||||
...newUpdatedOtherObjectFlatFieldMetadatas,
|
||||
],
|
||||
flatViewFieldToUpdate: [
|
||||
...flatViewFieldToUpdate,
|
||||
...newFlatViewFieldToUpdate,
|
||||
],
|
||||
flatViewFieldToCreate: [
|
||||
...flatViewFieldToCreate,
|
||||
...newFlatViewFieldToCreate,
|
||||
],
|
||||
flatIndexMetadataToUpdate: [
|
||||
...flatIndexMetadataToUpdate,
|
||||
...newUpdatedFlatIndexMetadatas,
|
||||
],
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
};
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { recomputeIndexAfterFlatObjectMetadataSingularNameUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-index-after-flat-object-metadata-singular-name-update.util';
|
||||
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
|
||||
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
|
||||
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
|
||||
updateObjectInput: UpdateOneObjectInput;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatIndexMaps' | 'flatObjectMetadataMaps' | 'flatFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
const objectMetadataEditableProperties =
|
||||
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
|
||||
(
|
||||
property,
|
||||
): property is Exclude<
|
||||
FlatObjectMetadataPropertiesToCompare,
|
||||
'standardOverrides'
|
||||
> => property !== 'standardOverrides',
|
||||
);
|
||||
|
||||
type UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
otherObjectFlatFieldMetadataToUpdate: FlatFieldMetadata[];
|
||||
flatIndexMetadataToUpdate: FlatIndexMetadata[];
|
||||
};
|
||||
|
||||
export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
updateObjectInput: rawUpdateObjectInput,
|
||||
flatIndexMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndOtherObjectFieldMetadatas => {
|
||||
const { id: objectMetadataIdToUpdate } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateObjectInput,
|
||||
['id'],
|
||||
);
|
||||
const updatedEditableObjectProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateObjectInput.update,
|
||||
objectMetadataEditableProperties,
|
||||
);
|
||||
|
||||
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityMaps: existingFlatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataIdToUpdate,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadataToUpdate)) {
|
||||
throw new ObjectMetadataException(
|
||||
'Object to update not found',
|
||||
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (isStandardMetadata(flatObjectMetadataToUpdate)) {
|
||||
const invalidUpdatedProperties = Object.keys(
|
||||
updatedEditableObjectProperties,
|
||||
).filter(
|
||||
(property) =>
|
||||
!OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.includes(
|
||||
property as ObjectMetadataStandardOverridesProperties,
|
||||
),
|
||||
);
|
||||
|
||||
if (invalidUpdatedProperties.length > 0) {
|
||||
throw new ObjectMetadataException(
|
||||
`Cannot edit standard object metadata properties: ${invalidUpdatedProperties.join(', ')}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedStandardFlatObjectdMetadata =
|
||||
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce((acc, property) => {
|
||||
const isPropertyUpdated =
|
||||
updatedEditableObjectProperties[property] !== undefined;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
standardOverrides: {
|
||||
...acc.standardOverrides,
|
||||
...(isPropertyUpdated
|
||||
? { [property]: updatedEditableObjectProperties[property] }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
}, flatObjectMetadataToUpdate);
|
||||
|
||||
return {
|
||||
flatObjectMetadata: updatedStandardFlatObjectdMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: [],
|
||||
flatIndexMetadataToUpdate: [],
|
||||
};
|
||||
}
|
||||
|
||||
const initialAccumulator: UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
|
||||
flatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
otherObjectFlatFieldMetadataToUpdate: [],
|
||||
flatIndexMetadataToUpdate: [],
|
||||
};
|
||||
|
||||
return objectMetadataEditableProperties.reduce<UpdatedFlatObjectAndOtherObjectFieldMetadatas>(
|
||||
(
|
||||
{
|
||||
flatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: otherObjectFlatFieldMetadatas,
|
||||
flatIndexMetadataToUpdate,
|
||||
},
|
||||
property,
|
||||
) => {
|
||||
const updatedPropertyValue = updatedEditableObjectProperties[property];
|
||||
const isPropertyUpdated =
|
||||
updatedPropertyValue !== undefined &&
|
||||
flatObjectMetadata[property] !== updatedPropertyValue;
|
||||
|
||||
if (!isPropertyUpdated) {
|
||||
return {
|
||||
flatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: otherObjectFlatFieldMetadatas,
|
||||
flatIndexMetadataToUpdate,
|
||||
};
|
||||
}
|
||||
|
||||
const updatedFlatObjectMetadata = {
|
||||
...flatObjectMetadata,
|
||||
[property]: updatedPropertyValue,
|
||||
};
|
||||
|
||||
const newUpdatedOtherObjectFlatFieldMetadatas =
|
||||
property === 'nameSingular' || property === 'namePlural'
|
||||
? renameRelatedMorphFieldOnObjectNamesUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
fromFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
toFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
})
|
||||
: [];
|
||||
|
||||
const newUpdatedFlatIndexMetadatas =
|
||||
property === 'nameSingular'
|
||||
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
flatIndexMaps,
|
||||
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: [
|
||||
...otherObjectFlatFieldMetadatas,
|
||||
...newUpdatedOtherObjectFlatFieldMetadatas,
|
||||
],
|
||||
flatIndexMetadataToUpdate: [
|
||||
...flatIndexMetadataToUpdate,
|
||||
...newUpdatedFlatIndexMetadatas,
|
||||
],
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
};
|
||||
+6
-5
@@ -1,7 +1,7 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
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 { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
@@ -31,10 +31,11 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
|
||||
nameSingular: updatedSingularName,
|
||||
};
|
||||
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: optimisticFlatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
|
||||
return allRelatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
|
||||
type RecomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdateArgs = {
|
||||
existingFlatObjectMetadata: FlatObjectMetadata;
|
||||
updatedLabelIdentifierFieldMetadataId: string;
|
||||
} & Pick<AllFlatEntityMaps, 'flatViewFieldMaps' | 'flatViewMaps'>;
|
||||
|
||||
type FlatViewFieldToCreateAndUpdate = {
|
||||
flatViewFieldToCreate: FlatViewField[];
|
||||
flatViewFieldToUpdate: FlatViewField[];
|
||||
};
|
||||
export const recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate = ({
|
||||
existingFlatObjectMetadata,
|
||||
flatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
updatedLabelIdentifierFieldMetadataId,
|
||||
}: RecomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdateArgs): FlatViewFieldToCreateAndUpdate => {
|
||||
const flatViews = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatViewMaps,
|
||||
flatEntityIds: existingFlatObjectMetadata.viewIds,
|
||||
});
|
||||
|
||||
const accumulator: FlatViewFieldToCreateAndUpdate = {
|
||||
flatViewFieldToCreate: [],
|
||||
flatViewFieldToUpdate: [],
|
||||
};
|
||||
|
||||
for (const flatView of flatViews) {
|
||||
const flatViewFields = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: flatView.viewFieldIds,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
const labelMetadataIdentifierViewField = flatViewFields.find(
|
||||
(viewField) =>
|
||||
viewField.fieldMetadataId === updatedLabelIdentifierFieldMetadataId,
|
||||
);
|
||||
|
||||
const ascSortedViewFieldPositions = flatViewFields
|
||||
.map(({ position }) => position)
|
||||
.sort((a, b) => a - b);
|
||||
const lowestViewFieldPosition =
|
||||
ascSortedViewFieldPositions.length > 0
|
||||
? ascSortedViewFieldPositions[0]
|
||||
: 0;
|
||||
|
||||
if (!isDefined(labelMetadataIdentifierViewField)) {
|
||||
const viewFieldId = v4();
|
||||
const createdAt = new Date();
|
||||
const flatViewFieldToCreate: FlatViewField = {
|
||||
fieldMetadataId: updatedLabelIdentifierFieldMetadataId,
|
||||
position: lowestViewFieldPosition - 1,
|
||||
isVisible: true,
|
||||
size: DEFAULT_VIEW_FIELD_SIZE,
|
||||
viewId: flatView.id,
|
||||
workspaceId: flatView.workspaceId,
|
||||
id: viewFieldId,
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
universalIdentifier: viewFieldId,
|
||||
aggregateOperation: null,
|
||||
};
|
||||
|
||||
accumulator.flatViewFieldToCreate.push(flatViewFieldToCreate);
|
||||
} else if (
|
||||
labelMetadataIdentifierViewField.position > lowestViewFieldPosition
|
||||
) {
|
||||
const updatedFlatViewField = {
|
||||
...labelMetadataIdentifierViewField,
|
||||
position: lowestViewFieldPosition - 1,
|
||||
};
|
||||
|
||||
accumulator.flatViewFieldToUpdate.push(updatedFlatViewField);
|
||||
}
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
};
|
||||
+6
-5
@@ -6,9 +6,9 @@ import {
|
||||
import { computeMorphRelationFieldName } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
|
||||
@@ -24,10 +24,11 @@ export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: fromFlatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
const manyToOneMorphRelationFlatFieldMetadatas =
|
||||
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
|
||||
+49
-9
@@ -4,8 +4,10 @@ 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 { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-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 { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
@@ -14,12 +16,11 @@ import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view
|
||||
import { fromCreateViewFieldInputToFlatViewFieldToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-field-input-to-flat-view-field-to-create.util';
|
||||
import { fromCreateViewInputToFlatViewToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-input-to-flat-view-to-create.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util';
|
||||
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
|
||||
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
|
||||
import { fromUpdateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-update-object-input-to-flat-object-metadata.util';
|
||||
import { fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-update-object-input-to-flat-object-metadata-and-related-flat-entities.util';
|
||||
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
||||
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
@@ -55,6 +56,8 @@ export class ObjectMetadataServiceV2 {
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatIndexMaps: existingFlatIndexMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -62,6 +65,8 @@ export class ObjectMetadataServiceV2 {
|
||||
'flatObjectMetadataMaps',
|
||||
'flatIndexMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -70,11 +75,15 @@ export class ObjectMetadataServiceV2 {
|
||||
flatObjectMetadata: optimisticallyUpdatedFlatObjectMetadata,
|
||||
otherObjectFlatFieldMetadataToUpdate: otherObjectFlatFieldMetadatas,
|
||||
flatIndexMetadataToUpdate,
|
||||
} = fromUpdateObjectInputToFlatObjectMetadata({
|
||||
flatViewFieldToUpdate,
|
||||
flatViewFieldToCreate,
|
||||
} = fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities({
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
updateObjectInput,
|
||||
flatIndexMaps: existingFlatIndexMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
@@ -106,6 +115,27 @@ export class ObjectMetadataServiceV2 {
|
||||
}),
|
||||
);
|
||||
|
||||
const flatViewFieldMapsWithUpdated = flatViewFieldToUpdate.reduce(
|
||||
(flatViewFieldMaps, flatViewField) =>
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewField,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
}),
|
||||
getSubFlatEntityMapsOrThrow({
|
||||
flatEntityIds: flatViewFieldToUpdate.map(({ id }) => id),
|
||||
flatEntityMaps: existingFlatViewFieldMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
const toFlatViewFieldMaps = flatViewFieldToCreate.reduce(
|
||||
(flatViewFieldMaps, flatViewField) =>
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatViewField,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
}),
|
||||
flatViewFieldMapsWithUpdated,
|
||||
);
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
@@ -122,6 +152,13 @@ export class ObjectMetadataServiceV2 {
|
||||
from: existingFlatFieldMetadataMaps,
|
||||
to: toFlatFieldMetadataMaps,
|
||||
},
|
||||
flatViewFieldMaps: {
|
||||
from: existingFlatViewFieldMaps,
|
||||
to: toFlatViewFieldMaps,
|
||||
},
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
@@ -329,7 +366,7 @@ export class ObjectMetadataServiceV2 {
|
||||
const toFlatFieldMetadataMaps = [
|
||||
...flatFieldMetadataToCreateOnObject,
|
||||
...relationTargetFlatFieldMetadataToCreate,
|
||||
].reduce(
|
||||
].reduce<FlatEntityMaps<FlatFieldMetadata>>(
|
||||
(flatFieldMaps, flatField) =>
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatField,
|
||||
@@ -353,13 +390,16 @@ export class ObjectMetadataServiceV2 {
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps: toFlatFieldMetadataMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
});
|
||||
const flatDefaultViewFieldsToCreate =
|
||||
await this.createDefaultFlatViewFields({
|
||||
objectFlatFieldMetadatas,
|
||||
objectFlatFieldMetadatas: findManyFlatEntityByIdInFlatEntityMapsOrThrow(
|
||||
{
|
||||
flatEntityMaps: toFlatFieldMetadataMaps,
|
||||
flatEntityIds: flatFieldMetadataToCreateOnObject.map(
|
||||
({ id }) => id,
|
||||
),
|
||||
},
|
||||
),
|
||||
viewId: flatDefaultViewToCreate.id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
+8
-3
@@ -13,6 +13,7 @@ import {
|
||||
|
||||
import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/workspace-query-builder/types/workspace-entity-duplicate-criteria.type';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
@@ -145,8 +146,7 @@ export class ObjectMetadataEntity implements Required<ObjectMetadataEntity> {
|
||||
|
||||
@OneToMany(
|
||||
() => ObjectPermissionEntity,
|
||||
(objectPermission: ObjectPermissionEntity) =>
|
||||
objectPermission.objectMetadata,
|
||||
(objectPermission) => objectPermission.objectMetadata,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
@@ -155,10 +155,15 @@ export class ObjectMetadataEntity implements Required<ObjectMetadataEntity> {
|
||||
|
||||
@OneToMany(
|
||||
() => FieldPermissionEntity,
|
||||
(fieldPermission: FieldPermissionEntity) => fieldPermission.objectMetadata,
|
||||
(fieldPermission) => fieldPermission.objectMetadata,
|
||||
{
|
||||
cascade: true,
|
||||
},
|
||||
)
|
||||
fieldPermissions: Relation<FieldPermissionEntity[]>;
|
||||
|
||||
@OneToMany(() => ViewEntity, (view) => view.objectMetadata, {
|
||||
cascade: true,
|
||||
})
|
||||
views: Relation<ViewEntity[]>;
|
||||
}
|
||||
|
||||
+8
@@ -27,6 +27,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const idField: FlatFieldMetadata<FieldMetadataType.UUID> = {
|
||||
type: FieldMetadataType.UUID,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: true,
|
||||
objectMetadataId,
|
||||
@@ -60,6 +61,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const nameField: FlatFieldMetadata<FieldMetadataType.TEXT> = {
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -93,6 +95,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const createdAtField: FlatFieldMetadata<FieldMetadataType.DATE_TIME> = {
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -126,6 +129,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const updatedAtField: FlatFieldMetadata<FieldMetadataType.DATE_TIME> = {
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -159,6 +163,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const deletedAtField: FlatFieldMetadata<FieldMetadataType.DATE_TIME> = {
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -192,6 +197,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const createdByField: FlatFieldMetadata<FieldMetadataType.ACTOR> = {
|
||||
type: FieldMetadataType.ACTOR,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -225,6 +231,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
const positionField: FlatFieldMetadata<FieldMetadataType.POSITION> = {
|
||||
type: FieldMetadataType.POSITION,
|
||||
id: v4(),
|
||||
viewFieldIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -257,6 +264,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
|
||||
const searchVectorField: FlatFieldMetadata<FieldMetadataType.TS_VECTOR> = {
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
viewFieldIds: [],
|
||||
id: v4(),
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
|
||||
+2
@@ -63,6 +63,7 @@ const generateSourceFlatFieldMetadata = ({
|
||||
}
|
||||
|
||||
return {
|
||||
viewFieldIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
defaultValue: null,
|
||||
@@ -127,6 +128,7 @@ const generateTargetFlatFieldMetadata = ({
|
||||
|
||||
return {
|
||||
morphId: null,
|
||||
viewFieldIds: [],
|
||||
id: sourceFlatFieldMetadata.relationTargetFieldMetadataId,
|
||||
name: sourceFlatObjectMetadata.nameSingular,
|
||||
label: sourceFlatObjectMetadata.labelSingular,
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type CachedFieldMetadataEntity } from 'src/engine/metadata-modules/types/cached-field-metadata-entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
export const getFieldMetadataEntityFromCachedObjectMetadataMaps = ({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataId: string;
|
||||
fieldMetadataId: string;
|
||||
}): FieldMetadataEntity | undefined => {
|
||||
const objectMetadataFromCache = objectMetadataMaps.byId[objectMetadataId];
|
||||
|
||||
if (!isDefined(objectMetadataFromCache)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fieldMetadataFromCache: CachedFieldMetadataEntity =
|
||||
objectMetadataFromCache.fieldsById[fieldMetadataId];
|
||||
|
||||
if (!isDefined(fieldMetadataFromCache)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We need to determine if graphql will be able to handle relations crafting byitself
|
||||
return {
|
||||
...fieldMetadataFromCache,
|
||||
object: {} as ObjectMetadataEntity,
|
||||
relationTargetFieldMetadata: null,
|
||||
relationTargetObjectMetadata: null,
|
||||
indexFieldMetadatas: {} as IndexFieldMetadataEntity,
|
||||
fieldPermissions: [],
|
||||
createdAt: new Date(fieldMetadataFromCache.createdAt),
|
||||
updatedAt: new Date(fieldMetadataFromCache.updatedAt),
|
||||
};
|
||||
};
|
||||
+8
-2
@@ -14,9 +14,9 @@ import {
|
||||
import { aggregateOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report.util';
|
||||
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
|
||||
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/workspace-migration-v2-object-actions-builder.service';
|
||||
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';
|
||||
@@ -224,6 +224,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
|
||||
optimisticAllFlatEntityMaps.flatViewMaps =
|
||||
viewResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps =
|
||||
viewResult.dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
if (viewResult.status === 'fail') {
|
||||
orchestratorFailureReport.view.push(...viewResult.errors);
|
||||
@@ -242,6 +244,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
to: toFlatViewFieldMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
@@ -254,6 +258,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
viewFieldResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatViewMaps =
|
||||
viewFieldResult.dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps =
|
||||
viewFieldResult.dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps;
|
||||
|
||||
if (viewFieldResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewField.push(...viewFieldResult.errors);
|
||||
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
@@ -5,7 +7,6 @@ import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-meta
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { type ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
|
||||
export type MetadataEntitiesRelationTarget =
|
||||
| ObjectMetadataEntity
|
||||
@@ -15,4 +16,5 @@ export type MetadataEntitiesRelationTarget =
|
||||
| DataSourceEntity
|
||||
| ApplicationEntity
|
||||
| IndexMetadataEntity
|
||||
| ObjectPermissionEntity;
|
||||
| ObjectPermissionEntity
|
||||
| ViewEntity;
|
||||
|
||||
-3
@@ -19,9 +19,6 @@ export type ObjectMetadataRelatedFlatEntityMaps = Pick<
|
||||
'flatFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
// TODO find a solution in order to handle label indentifier validation
|
||||
// export type ObjectMetadataRelatedFlatEntityMaps = undefined;
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'objectMetadata',
|
||||
+1
-1
@@ -2,5 +2,5 @@ import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all
|
||||
|
||||
export type ViewFieldRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatViewMaps' | 'flatFieldMetadataMaps'
|
||||
'flatViewMaps' | 'flatFieldMetadataMaps' | 'flatObjectMetadataMaps'
|
||||
>;
|
||||
|
||||
+43
-5
@@ -48,7 +48,7 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
await this.flatViewFieldValidatorService.validateFlatViewFieldCreation({
|
||||
this.flatViewFieldValidatorService.validateFlatViewFieldCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatViewFieldToValidate,
|
||||
optimisticFlatViewFieldMaps,
|
||||
@@ -61,12 +61,30 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
};
|
||||
}
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatFieldMetadata,
|
||||
viewFieldIds: [
|
||||
...flatFieldMetadata.viewFieldIds,
|
||||
flatViewFieldToValidate.id,
|
||||
],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFieldToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewFields = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
const updatedFlatViewMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
viewFieldIds: [...flatView.viewFieldIds, flatViewFieldToValidate.id],
|
||||
@@ -82,7 +100,8 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewFields,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -115,12 +134,30 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
};
|
||||
}
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadataMaps = isDefined(flatFieldMetadata)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatFieldMetadata,
|
||||
viewFieldIds: flatFieldMetadata.viewFieldIds.filter(
|
||||
(id) => id !== flatViewFieldToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps;
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFieldToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewFields = isDefined(flatView)
|
||||
const updatedFlatViewMaps = isDefined(flatView)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
@@ -140,7 +177,8 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewFields,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+42
-2
@@ -1,6 +1,11 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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 { 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 { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
import { compareTwoFlatView } from 'src/engine/core-modules/view/flat-view/utils/compare-two-flat-view.util';
|
||||
import {
|
||||
@@ -56,6 +61,19 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
viewIds: [...flatObjectMetadata.viewIds, flatViewToValidate.id],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
@@ -63,7 +81,9 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
type: 'create_view',
|
||||
view: flatViewToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,13 +112,33 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadataMaps = isDefined(flatObjectMetadata)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
viewIds: flatObjectMetadata.viewIds.filter(
|
||||
(id) => id !== flatViewToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_view',
|
||||
viewId: flatViewToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -15,14 +15,16 @@ export type CreateViewFieldAction = {
|
||||
viewField: FlatViewField;
|
||||
};
|
||||
|
||||
export type FlatViewFieldPropertiesUpdates = Array<
|
||||
{
|
||||
[P in FlatViewFieldPropertiesToCompare]: FlatViewFieldPropertyUpdate<P>;
|
||||
}[FlatViewFieldPropertiesToCompare]
|
||||
>;
|
||||
|
||||
export type UpdateViewFieldAction = {
|
||||
type: 'update_view_field';
|
||||
viewFieldId: string;
|
||||
updates: Array<
|
||||
{
|
||||
[P in FlatViewFieldPropertiesToCompare]: FlatViewFieldPropertyUpdate<P>;
|
||||
}[FlatViewFieldPropertiesToCompare]
|
||||
>;
|
||||
updates: FlatViewFieldPropertiesUpdates;
|
||||
};
|
||||
|
||||
export type DeleteViewFieldAction = {
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modul
|
||||
import { validateFlatObjectMetadataNameAndLabels } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { ObjectMetadataRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { ObjectMetadataRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/workspace-migration-v2-object-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
|
||||
import { FlatObjectPropertiesUpdates } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
+164
-55
@@ -4,11 +4,14 @@ import { 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 { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { isViewFieldInLowestPosition } from 'src/engine/core-modules/view/flat-view/utils/is-view-field-in-lowest-position.util';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/types/view-field-related-flat-entity-maps.type';
|
||||
import { validateLabelIdentifierFieldMetadataIdFlatViewField } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/utils/validate-label-identifier-field-metadata-id-flat-view-field.util';
|
||||
|
||||
type ViewFieldValidationArgs = {
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
@@ -19,53 +22,119 @@ type ViewFieldValidationArgs = {
|
||||
export class FlatViewFieldValidatorService {
|
||||
constructor() {}
|
||||
|
||||
// Should implement strict checking on field compared field and their integrity passing the update array would be the best
|
||||
public validateFlatViewFieldUpdate({
|
||||
flatViewFieldToValidate: updatedFlatViewField,
|
||||
optimisticFlatViewFieldMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatViewMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
}: ViewFieldValidationArgs): FailedFlatEntityValidation<FlatViewField> {
|
||||
const errors = [];
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewField> = {
|
||||
type: 'update_view_field',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: updatedFlatViewField.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatViewField =
|
||||
optimisticFlatViewFieldMaps.byId[updatedFlatViewField.id];
|
||||
|
||||
if (!isDefined(existingFlatViewField)) {
|
||||
errors.push({
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field to update not found`,
|
||||
userFriendlyMessage: t`View field to update not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'update_view_field',
|
||||
errors,
|
||||
flatEntityMinimalInformation: {
|
||||
id: updatedFlatViewField.id,
|
||||
viewId: updatedFlatViewField.viewId,
|
||||
fieldMetadataId: updatedFlatViewField.fieldMetadataId,
|
||||
},
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: updatedFlatViewField.id,
|
||||
viewId: updatedFlatViewField.viewId,
|
||||
fieldMetadataId: updatedFlatViewField.fieldMetadataId,
|
||||
};
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: updatedFlatViewField.viewId,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatView)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field to update parent view not found`,
|
||||
userFriendlyMessage: t`View field to update parent view not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatView.objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field to update parent view object not found`,
|
||||
userFriendlyMessage: t`View field to update parent view object not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
if (
|
||||
flatObjectMetadata.labelIdentifierFieldMetadataId ===
|
||||
updatedFlatViewField.fieldMetadataId
|
||||
) {
|
||||
const otherFlatViewFields = findManyFlatEntityByIdInFlatEntityMapsOrThrow(
|
||||
{
|
||||
flatEntityIds: flatView.viewFieldIds.filter(
|
||||
(flatViewId) => flatViewId !== updatedFlatViewField.id,
|
||||
),
|
||||
flatEntityMaps: optimisticFlatViewFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateLabelIdentifierFieldMetadataIdFlatViewField({
|
||||
otherFlatViewFields,
|
||||
flatViewFieldToValidate: updatedFlatViewField,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public validateFlatViewFieldDeletion({
|
||||
optimisticFlatViewFieldMaps,
|
||||
flatViewFieldToValidate: { id: viewFieldIdToDelete },
|
||||
}: ViewFieldValidationArgs): FailedFlatEntityValidation<FlatViewField> {
|
||||
const errors = [];
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewField> = {
|
||||
type: 'delete_view_field',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: viewFieldIdToDelete,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatViewField =
|
||||
optimisticFlatViewFieldMaps.byId[viewFieldIdToDelete];
|
||||
|
||||
if (!isDefined(existingFlatViewField)) {
|
||||
errors.push({
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field to delete not found`,
|
||||
userFriendlyMessage: t`View field to delete not found`,
|
||||
});
|
||||
} else {
|
||||
if (!isDefined(existingFlatViewField.deletedAt)) {
|
||||
errors.push({
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field to delete has not been soft deleted`,
|
||||
userFriendlyMessage: t`View field to delete has not been soft deleted`,
|
||||
@@ -73,28 +142,31 @@ export class FlatViewFieldValidatorService {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'delete_view_field',
|
||||
errors,
|
||||
flatEntityMinimalInformation: {
|
||||
id: viewFieldIdToDelete,
|
||||
},
|
||||
};
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public async validateFlatViewFieldCreation({
|
||||
public validateFlatViewFieldCreation({
|
||||
flatViewFieldToValidate,
|
||||
optimisticFlatViewFieldMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatViewMaps: optimisticFlatViewMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
}: {
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
optimisticFlatViewFieldMaps: FlatViewFieldMaps;
|
||||
dependencyOptimisticFlatEntityMaps: ViewFieldRelatedFlatEntityMaps;
|
||||
}): Promise<FailedFlatEntityValidation<FlatViewField>> {
|
||||
const errors = [];
|
||||
}): FailedFlatEntityValidation<FlatViewField> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatViewField> = {
|
||||
type: 'create_view_field',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewFieldToValidate.id,
|
||||
viewId: flatViewFieldToValidate.viewId,
|
||||
fieldMetadataId: flatViewFieldToValidate.fieldMetadataId,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatViewField =
|
||||
optimisticFlatViewFieldMaps.byId[flatViewFieldToValidate.id];
|
||||
@@ -102,63 +174,100 @@ export class FlatViewFieldValidatorService {
|
||||
if (isDefined(existingFlatViewField)) {
|
||||
const flatViewFieldId = flatViewFieldToValidate.id;
|
||||
|
||||
errors.push({
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field metadata with id ${flatViewFieldId} already exists`,
|
||||
userFriendlyMessage: t`View field metadata already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
const relatedFlatView =
|
||||
optimisticFlatViewMaps.byId[flatViewFieldToValidate.viewId];
|
||||
|
||||
if (!isDefined(relatedFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const relatedFlatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
errors.push({
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Field metadata not found`,
|
||||
userFriendlyMessage: t`Field metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const allFlatViewFields = Object.values(
|
||||
optimisticFlatViewFieldMaps.byId,
|
||||
).filter(isDefined);
|
||||
const matchingFlatView = allFlatViewFields.find(
|
||||
const flatView = flatViewMaps.byId[flatViewFieldToValidate.viewId];
|
||||
|
||||
if (!isDefined(flatView)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const otherFlatViewFields = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: flatView.viewFieldIds,
|
||||
flatEntityMaps: optimisticFlatViewFieldMaps,
|
||||
});
|
||||
const equivalentExistingFlatViewFieldExists = otherFlatViewFields.some(
|
||||
(flatViewField) =>
|
||||
flatViewField.viewId === flatViewFieldToValidate.viewId &&
|
||||
flatViewField.fieldMetadataId ===
|
||||
flatViewFieldToValidate.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(matchingFlatView)) {
|
||||
errors.push({
|
||||
if (equivalentExistingFlatViewFieldExists) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field with same fieldmetadataId and viewId already exists`,
|
||||
userFriendlyMessage: t`View field already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'create_view_field',
|
||||
errors,
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewFieldToValidate.id,
|
||||
viewId: flatViewFieldToValidate.viewId,
|
||||
fieldMetadataId: flatViewFieldToValidate.fieldMetadataId,
|
||||
},
|
||||
};
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: flatView.objectMetadataId,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field related view object metadata not found`,
|
||||
userFriendlyMessage: t`View field related view object metadata not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
if (
|
||||
flatObjectMetadata.labelIdentifierFieldMetadataId ===
|
||||
flatViewFieldToValidate.fieldMetadataId
|
||||
) {
|
||||
validationResult.errors.push(
|
||||
...validateLabelIdentifierFieldMetadataIdFlatViewField({
|
||||
flatViewFieldToValidate,
|
||||
otherFlatViewFields,
|
||||
}),
|
||||
);
|
||||
} else if (
|
||||
otherFlatViewFields.some(
|
||||
(flatViewField) =>
|
||||
flatViewField.fieldMetadataId ===
|
||||
flatObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
) &&
|
||||
isViewFieldInLowestPosition({
|
||||
flatViewField: flatViewFieldToValidate,
|
||||
otherFlatViewFields,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View field position cannot be lower than label identifier view field position`,
|
||||
userFriendlyMessage: t`View field position cannot be lower than label identifier view field position`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { isViewFieldInLowestPosition } from 'src/engine/core-modules/view/flat-view/utils/is-view-field-in-lowest-position.util';
|
||||
import { type FlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
type ValidateLabelIdentifierFieldMetadataIdFlatViewFieldArgs = {
|
||||
otherFlatViewFields: FlatViewField[];
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
};
|
||||
export const validateLabelIdentifierFieldMetadataIdFlatViewField = ({
|
||||
otherFlatViewFields,
|
||||
flatViewFieldToValidate,
|
||||
}: ValidateLabelIdentifierFieldMetadataIdFlatViewFieldArgs): FlatEntityValidationError[] => {
|
||||
const errors: FlatEntityValidationError[] = [];
|
||||
|
||||
if (
|
||||
otherFlatViewFields.length > 0 &&
|
||||
!isViewFieldInLowestPosition({
|
||||
flatViewField: flatViewFieldToValidate,
|
||||
otherFlatViewFields,
|
||||
})
|
||||
) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Label identifier view field has to be in the lowest position`,
|
||||
userFriendlyMessage: t`Label identifier view field has to be in the lowest position`,
|
||||
});
|
||||
}
|
||||
|
||||
if (flatViewFieldToValidate.isVisible === false) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Label identifier view field has to be visible`,
|
||||
userFriendlyMessage: t`Label identifier view field has to be visible`,
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(flatViewFieldToValidate.deletedAt)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Label identifier view field cannot be deleted`,
|
||||
userFriendlyMessage: t`Label identifier view field cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
+2
-2
@@ -4,9 +4,9 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
|
||||
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
|
||||
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
|
||||
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/workspace-migration-v2-object-actions-builder.service';
|
||||
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';
|
||||
|
||||
+6
-5
@@ -8,7 +8,7 @@ import {
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -90,10 +90,11 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
schemaName,
|
||||
tableName,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
const enumOrCompositeFlatFieldMetadatas = objectFlatFieldMetadatas.filter(
|
||||
(field) =>
|
||||
isEnumFlatFieldMetadata(field) || isCompositeFlatFieldMetadata(field),
|
||||
|
||||
+5
-5
@@ -7,8 +7,8 @@ import {
|
||||
|
||||
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 { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-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 { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -121,10 +121,10 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
newTableName,
|
||||
});
|
||||
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: updatedObjectMetadata,
|
||||
const objectFlatFieldMetadatas =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityIds: updatedObjectMetadata.fieldMetadataIds,
|
||||
});
|
||||
const enumOrCompositeFlatFieldMetadatas =
|
||||
objectFlatFieldMetadatas.filter(
|
||||
|
||||
+8
-2
@@ -85,7 +85,6 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
this.logger.timeEnd('Runner', 'Transaction execution');
|
||||
this.logger.time('Runner', 'Cache invalidation');
|
||||
|
||||
const flatEntitiesCacheToInvalidate = [
|
||||
...new Set([
|
||||
@@ -94,6 +93,10 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
]),
|
||||
];
|
||||
|
||||
this.logger.time(
|
||||
'Runner',
|
||||
`Cache invalidation ${flatEntitiesCacheToInvalidate.join()}`,
|
||||
);
|
||||
if (
|
||||
flatEntitiesCacheToInvalidate.includes('flatObjectMetadataMaps') ||
|
||||
flatEntitiesCacheToInvalidate.includes('flatFieldMetadataMaps')
|
||||
@@ -119,7 +122,10 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
],
|
||||
});
|
||||
|
||||
this.logger.timeEnd('Runner', 'Cache invalidation');
|
||||
this.logger.timeEnd(
|
||||
'Runner',
|
||||
`Cache invalidation ${flatEntitiesCacheToInvalidate.join()}`,
|
||||
);
|
||||
this.logger.timeEnd('Runner', 'Total execution');
|
||||
|
||||
return allFlatEntityMaps;
|
||||
|
||||
+2
@@ -137,6 +137,7 @@ export class StandardFieldFactory {
|
||||
|
||||
return [
|
||||
{
|
||||
viewFields: [],
|
||||
type: workspaceFieldMetadataArgs.type,
|
||||
standardId: workspaceFieldMetadataArgs.standardId,
|
||||
name: workspaceFieldMetadataArgs.name,
|
||||
@@ -186,6 +187,7 @@ export class StandardFieldFactory {
|
||||
}
|
||||
|
||||
fieldMetadataCollection.push({
|
||||
viewFields: [],
|
||||
type: FieldMetadataType.RELATION,
|
||||
standardId: workspaceRelationMetadataArgs.standardId,
|
||||
name: workspaceRelationMetadataArgs.name,
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ export const computeStandardFields = (
|
||||
fields.push({
|
||||
...data,
|
||||
...rest,
|
||||
viewFields: [],
|
||||
standardId: relationStandardId,
|
||||
defaultValue: null,
|
||||
isNullable: true,
|
||||
|
||||
@@ -19,6 +19,7 @@ export const getMockFieldMetadataEntity = <
|
||||
overrides: GetMockFieldMetadataEntityOverride<T>,
|
||||
): FieldMetadataEntity => {
|
||||
return {
|
||||
viewFields: [],
|
||||
morphId: null,
|
||||
fieldPermissions: [],
|
||||
icon: null,
|
||||
|
||||
@@ -46,6 +46,7 @@ export const getMockObjectMetadataEntity = (
|
||||
targetRelationFields: [],
|
||||
standardOverrides: null,
|
||||
targetTableName: faker.string.uuid(),
|
||||
views: [],
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
import { createOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/create-one-core-view-field.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { type CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
|
||||
import {
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
type ViewFieldTestSetup,
|
||||
} from './utils/setup-view-field-test-v2.util';
|
||||
|
||||
describe('View Field Resolver - Failing Create Operation - v2', () => {
|
||||
let testSetup: ViewFieldTestSetup;
|
||||
|
||||
beforeAll(async () => {
|
||||
testSetup = await setupViewFieldTestV2();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanupViewFieldTestV2(testSetup.testObjectMetadataId);
|
||||
});
|
||||
|
||||
type CreateViewFieldTestCase = (testSetup: ViewFieldTestSetup) => {
|
||||
input: CreateViewFieldInput;
|
||||
};
|
||||
|
||||
const createViewFieldTestCases: EachTestingContext<CreateViewFieldTestCase>[] =
|
||||
[
|
||||
{
|
||||
title: 'non-existent field metadata',
|
||||
context: (testSetup) => ({
|
||||
input: {
|
||||
viewId: testSetup.testViewId,
|
||||
fieldMetadataId: faker.string.uuid(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: 'non-existent view metadata',
|
||||
context: (testSetup) => ({
|
||||
input: {
|
||||
viewId: faker.string.uuid(),
|
||||
fieldMetadataId: testSetup.testFieldMetadataId,
|
||||
},
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
it.each(eachTestingContextFilter(createViewFieldTestCases))(
|
||||
'should fail to create view field when $title',
|
||||
async ({ context }) => {
|
||||
const { input } = context(testSetup);
|
||||
const response = await createOneCoreViewField({
|
||||
input,
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expect(response.errors).toBeDefined();
|
||||
expect(response.errors.length).toBe(1);
|
||||
const [firstError] = response.errors;
|
||||
|
||||
expect(firstError).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(firstError),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
+52
@@ -1,5 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`View Field Resolver - Failing Create Operation - v2 Should fail to create a conflicting view field on view and field metadata 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View field with same fieldmetadataId and viewId already exists",
|
||||
"userFriendlyMessage": "View field already exists",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while creating view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`View Field Resolver - Failing Create Operation - v2 should fail to create view field when non-existent field metadata 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`View Field Resolver - Successful object metadata identifier update side effect on view field Should not allow creating a view field with a position lower than the label identifier view field 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View field position cannot be lower than label identifier view field position",
|
||||
"userFriendlyMessage": "View field position cannot be lower than label identifier view field position",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while creating view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`View Field Resolver - Successful object metadata identifier update side effect on view field Should not allow deleting a label identifier view field 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Label identifier view field cannot be deleted",
|
||||
"userFriendlyMessage": "Label identifier view field cannot be deleted",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while deleting view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`View Field Resolver - Successful object metadata identifier update side effect on view field Should not allow destroying a label identifier view field 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View field to delete has not been soft deleted",
|
||||
"userFriendlyMessage": "View field to delete has not been soft deleted",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"id": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "delete_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while deleting view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`View Field Resolver - Successful object metadata identifier update side effect on view field Should not allow updating a label identifier view field visibility to false 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Label identifier view field has to be visible",
|
||||
"userFriendlyMessage": "Label identifier view field has to be visible",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while updating view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`View Field Resolver - Successful object metadata identifier update side effect on view field Should not allow updating labelIdentifier view field with a position higher than existing other view field 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "BAD_USER_INPUT",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Label identifier view field has to be in the lowest position",
|
||||
"userFriendlyMessage": "Label identifier view field has to be in the lowest position",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while updating view field",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { createOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/create-one-core-view-field.util';
|
||||
import { deleteOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/delete-one-core-view-field.util';
|
||||
import { destroyOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/destroy-one-core-view-field.util';
|
||||
import { findCoreViewFields } from 'test/integration/metadata/suites/view-field/utils/find-core-view-fields.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
|
||||
import { type CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
|
||||
describe('View Field Resolver - Failing Create Operation - v2', () => {
|
||||
let testSetup: ViewFieldTestSetup;
|
||||
let createdFlatViewFieldIds: string[] = [];
|
||||
|
||||
beforeAll(async () => {
|
||||
testSetup = await setupViewFieldTestV2();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
for (const viewFieldId of createdFlatViewFieldIds) {
|
||||
await deleteOneCoreViewField({
|
||||
input: {
|
||||
id: viewFieldId,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await destroyOneCoreViewField({
|
||||
input: {
|
||||
id: viewFieldId,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanupViewFieldTestV2(testSetup.testObjectMetadataId);
|
||||
});
|
||||
|
||||
type CreateViewFieldTestCase = (testSetup: ViewFieldTestSetup) => {
|
||||
input: CreateViewFieldInput;
|
||||
};
|
||||
|
||||
const createViewFieldTestCases: EachTestingContext<CreateViewFieldTestCase>[] =
|
||||
[
|
||||
{
|
||||
title: 'non-existent field metadata',
|
||||
context: (testSetup) => ({
|
||||
input: {
|
||||
viewId: testSetup.testViewId,
|
||||
fieldMetadataId: faker.string.uuid(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: 'non-existent view metadata',
|
||||
context: (testSetup) => ({
|
||||
input: {
|
||||
viewId: faker.string.uuid(),
|
||||
fieldMetadataId: testSetup.testFieldMetadataId,
|
||||
},
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
it.each(eachTestingContextFilter(createViewFieldTestCases))(
|
||||
'should fail to create view field when $title',
|
||||
async ({ context }) => {
|
||||
const { input } = context(testSetup);
|
||||
const response = await createOneCoreViewField({
|
||||
input,
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expect(response.errors).toBeDefined();
|
||||
expect(response.errors.length).toBe(1);
|
||||
const [firstError] = response.errors;
|
||||
|
||||
expect(firstError.extensions.code).not.toBe('INTERNAL_SERVER_ERROR');
|
||||
expect(firstError).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(firstError),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it('Should fail to create a conflicting view field on view and field metadata', async () => {
|
||||
const viewFieldId = '20202020-7ace-42ee-aecf-2b1c1bd34bce';
|
||||
const {
|
||||
data: {
|
||||
createCoreViewField: { id: createdFlatViewFieldId },
|
||||
},
|
||||
} = await createOneCoreViewField({
|
||||
input: {
|
||||
id: viewFieldId,
|
||||
fieldMetadataId: testSetup.testFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
createdFlatViewFieldIds.push(createdFlatViewFieldId);
|
||||
|
||||
const {
|
||||
data: { getCoreViewFields },
|
||||
} = await findCoreViewFields({
|
||||
viewId: testSetup.testViewId,
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
fieldMetadataId
|
||||
viewId
|
||||
`,
|
||||
});
|
||||
|
||||
expect(getCoreViewFields).toStrictEqual([
|
||||
{
|
||||
id: viewFieldId,
|
||||
fieldMetadataId: testSetup.testFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
},
|
||||
]);
|
||||
|
||||
const response = await createOneCoreViewField({
|
||||
input: {
|
||||
fieldMetadataId: testSetup.testFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
},
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expect(response.errors).toBeDefined();
|
||||
expect(response.errors.length).toBe(1);
|
||||
const [firstError] = response.errors;
|
||||
|
||||
expect(firstError.extensions.code).not.toBe('INTERNAL_SERVER_ERROR');
|
||||
expect(firstError).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(firstError),
|
||||
);
|
||||
});
|
||||
});
|
||||
+5
-6
@@ -1,18 +1,17 @@
|
||||
import { TEST_NOT_EXISTING_VIEW_FIELD_ID } from 'test/integration/constants/test-view-ids.constants';
|
||||
import { deleteOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/delete-one-core-view-field.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
|
||||
import { type DeleteViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/delete-view-field.input';
|
||||
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
type ViewFieldTestSetup,
|
||||
} from './utils/setup-view-field-test-v2.util';
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
|
||||
import { type DeleteViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/delete-view-field.input';
|
||||
|
||||
describe('View Field Resolver - Failing Delete Operation - v2', () => {
|
||||
let testSetup: ViewFieldTestSetup;
|
||||
+5
-6
@@ -1,18 +1,17 @@
|
||||
import { TEST_NOT_EXISTING_VIEW_FIELD_ID } from 'test/integration/constants/test-view-ids.constants';
|
||||
import { destroyOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/destroy-one-core-view-field.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
|
||||
import { type DestroyViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/destroy-view-field.input';
|
||||
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
type ViewFieldTestSetup,
|
||||
} from './utils/setup-view-field-test-v2.util';
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
|
||||
import { type DestroyViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/destroy-view-field.input';
|
||||
|
||||
describe('View Field Resolver - Failing Destroy Operation - v2', () => {
|
||||
let testSetup: ViewFieldTestSetup;
|
||||
+5
-6
@@ -1,18 +1,17 @@
|
||||
import { TEST_NOT_EXISTING_VIEW_FIELD_ID } from 'test/integration/constants/test-view-ids.constants';
|
||||
import { updateOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/update-one-core-view-field.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
|
||||
import { type UpdateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/update-view-field.input';
|
||||
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
type ViewFieldTestSetup,
|
||||
} from './utils/setup-view-field-test-v2.util';
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
|
||||
import { type UpdateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/update-view-field.input';
|
||||
|
||||
describe('View Field Resolver - Failing Update Operation - v2', () => {
|
||||
let testSetup: ViewFieldTestSetup;
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/create-one-core-view-field.util';
|
||||
import { deleteOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/delete-one-core-view-field.util';
|
||||
import { destroyOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/destroy-one-core-view-field.util';
|
||||
import { findCoreViewFields } from 'test/integration/metadata/suites/view-field/utils/find-core-view-fields.util';
|
||||
import { updateOneCoreViewField } from 'test/integration/metadata/suites/view-field/utils/update-one-core-view-field.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
|
||||
describe('View Field Resolver - Successful object metadata identifier update side effect on view field', () => {
|
||||
let testSetup: ViewFieldTestSetup & {
|
||||
testLabelIdentifierViewFieldId: string;
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const { testFieldMetadataId, testObjectMetadataId, testViewId } =
|
||||
await setupViewFieldTestV2();
|
||||
|
||||
await updateOneObjectMetadata({
|
||||
input: {
|
||||
idToUpdate: testObjectMetadataId,
|
||||
updatePayload: {
|
||||
labelIdentifierFieldMetadataId: testFieldMetadataId,
|
||||
},
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewFields },
|
||||
} = await findCoreViewFields({
|
||||
viewId: testViewId,
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
fieldMetadataId
|
||||
viewId
|
||||
`,
|
||||
});
|
||||
const testLabelIdentifierViewFieldId = getCoreViewFields[0].id;
|
||||
|
||||
testSetup = {
|
||||
testFieldMetadataId,
|
||||
testObjectMetadataId,
|
||||
testViewId,
|
||||
testLabelIdentifierViewFieldId,
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanupViewFieldTestV2(testSetup.testObjectMetadataId);
|
||||
});
|
||||
|
||||
it('should create a view field on label identifier object metadata update if it does not exist on view', async () => {
|
||||
const { testFieldMetadataId, testViewId } = testSetup;
|
||||
const {
|
||||
data: { getCoreViewFields },
|
||||
} = await findCoreViewFields({
|
||||
viewId: testViewId,
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
fieldMetadataId
|
||||
viewId
|
||||
`,
|
||||
});
|
||||
|
||||
expect(getCoreViewFields).toMatchObject<
|
||||
Pick<FlatViewField, 'id' | 'fieldMetadataId' | 'viewId'>[]
|
||||
>([
|
||||
{
|
||||
id: testSetup.testLabelIdentifierViewFieldId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
viewId: testViewId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('Should not allow deleting a label identifier view field', async () => {
|
||||
const { errors } = await deleteOneCoreViewField({
|
||||
input: { id: testSetup.testLabelIdentifierViewFieldId },
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not allow destroying a label identifier view field', async () => {
|
||||
const { errors } = await destroyOneCoreViewField({
|
||||
input: { id: testSetup.testLabelIdentifierViewFieldId },
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not allow updating a label identifier view field visibility to false', async () => {
|
||||
const { errors } = await updateOneCoreViewField({
|
||||
input: {
|
||||
id: testSetup.testLabelIdentifierViewFieldId,
|
||||
update: { isVisible: false },
|
||||
},
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not allow creating a view field with a position lower than the label identifier view field', async () => {
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: fieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'nonLabelIdentifierFieldFailing',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: testSetup.testObjectMetadataId,
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
`,
|
||||
});
|
||||
|
||||
const { errors } = await createOneCoreViewField({
|
||||
input: {
|
||||
fieldMetadataId: fieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: -42,
|
||||
},
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not allow updating labelIdentifier view field with a position higher than existing other view field', async () => {
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: fieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'nonLabelIdentifierFieldSuccessful',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: testSetup.testObjectMetadataId,
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
`,
|
||||
});
|
||||
|
||||
await createOneCoreViewField({
|
||||
input: {
|
||||
fieldMetadataId: fieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 42,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const { errors } = await updateOneCoreViewField({
|
||||
input: {
|
||||
id: testSetup.testLabelIdentifierViewFieldId,
|
||||
update: {
|
||||
position: 43,
|
||||
},
|
||||
},
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should update labelIdentifier view field with a position higher than existing other view field', async () => {
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: fieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'justafield',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: testSetup.testObjectMetadataId,
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
`,
|
||||
});
|
||||
|
||||
await createOneCoreViewField({
|
||||
input: {
|
||||
fieldMetadataId: fieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 42,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await updateOneCoreViewField({
|
||||
input: {
|
||||
id: testSetup.testLabelIdentifierViewFieldId,
|
||||
update: {
|
||||
position: 41,
|
||||
},
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
+5
-6
@@ -7,16 +7,15 @@ import {
|
||||
eachTestingContextFilter,
|
||||
} from 'twenty-shared/testing';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type ViewFieldTestSetup,
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
} from 'test/integration/graphql/suites/view/utils/setup-view-field-test-v2.util';
|
||||
|
||||
import { type CreateViewFieldInput } from 'src/engine/core-modules/view/dtos/inputs/create-view-field.input';
|
||||
import { type ViewFieldDTO } from 'src/engine/core-modules/view/dtos/view-field.dto';
|
||||
|
||||
import {
|
||||
cleanupViewFieldTestV2,
|
||||
setupViewFieldTestV2,
|
||||
type ViewFieldTestSetup,
|
||||
} from './utils/setup-view-field-test-v2.util';
|
||||
|
||||
type TestContext = {
|
||||
viewFieldInput: (testSetup: ViewFieldTestSetup) => CreateViewFieldInput;
|
||||
expected: Partial<ViewFieldDTO>;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
|
||||
import { type BaseGraphQLError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
export const expectOneNotInternalServerErrorSnapshot = ({
|
||||
errors,
|
||||
}: {
|
||||
errors: BaseGraphQLError[];
|
||||
}) => {
|
||||
expect(errors.length).toBe(1);
|
||||
const [firstError] = errors;
|
||||
|
||||
expect(firstError.extensions.code).not.toBe('INTERNAL_SERVER_ERROR');
|
||||
expect(firstError).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(firstError),
|
||||
);
|
||||
};
|
||||
+4893
-21
File diff suppressed because it is too large
Load Diff
+41
-2
@@ -31,7 +31,24 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View field position cannot be lower than label identifier view field position",
|
||||
"userFriendlyMessage": "View field position cannot be lower than label identifier view field position",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
@@ -85,7 +102,29 @@ exports[`Object metadata update should fail when labelIdentifier is not a known
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [],
|
||||
"viewField": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Field metadata not found",
|
||||
"userFriendlyMessage": "Field metadata not found",
|
||||
},
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View field position cannot be lower than label identifier view field position",
|
||||
"userFriendlyMessage": "View field position cannot be lower than label identifier view field position",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_field",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
|
||||
+2
@@ -90,6 +90,7 @@ describe('Custom object renaming', () => {
|
||||
|
||||
// Act
|
||||
const { data } = await createOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: CUSTOM_OBJECT,
|
||||
gqlFields: `
|
||||
id
|
||||
@@ -141,6 +142,7 @@ describe('Custom object renaming', () => {
|
||||
|
||||
// Act
|
||||
const { data } = await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
nameSingular
|
||||
labelSingular
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import { CUSTOM_OBJECT_DISHES } from 'test/integration/metadata/suites/object-metadata/constants/custom-object-dishes.constants';
|
||||
import { type CreateOneObjectFactoryInput } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { findCoreViewFields } from 'test/integration/metadata/suites/view-field/utils/find-core-view-fields.util';
|
||||
import { findCoreViews } from 'test/integration/metadata/suites/view/utils/find-core-views.util';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
|
||||
|
||||
describe('View side effect on object creation', () => {
|
||||
let createdObjectMetadataId: string | undefined = undefined;
|
||||
|
||||
afterEach(async () => {
|
||||
if (!isDefined(createdObjectMetadataId)) {
|
||||
return;
|
||||
}
|
||||
await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdObjectMetadataId,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await deleteOneObjectMetadata({
|
||||
input: { idToDelete: createdObjectMetadataId },
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
createdObjectMetadataId = undefined;
|
||||
});
|
||||
it('should create a view and view field for default custom objects standard fields on object metadata creation', async () => {
|
||||
const {
|
||||
labelPlural,
|
||||
description,
|
||||
labelSingular,
|
||||
namePlural,
|
||||
nameSingular,
|
||||
} = CUSTOM_OBJECT_DISHES;
|
||||
const createObjectInput: CreateOneObjectFactoryInput = {
|
||||
labelPlural,
|
||||
description,
|
||||
labelSingular,
|
||||
namePlural,
|
||||
nameSingular,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isLabelSyncedWithName: false,
|
||||
};
|
||||
const objectCreationGqlFields = `
|
||||
id
|
||||
labelPlural
|
||||
description
|
||||
labelSingular
|
||||
namePlural
|
||||
nameSingular
|
||||
icon
|
||||
isLabelSyncedWithName
|
||||
`;
|
||||
|
||||
const {
|
||||
data: { createOneObject },
|
||||
} = await createOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: createObjectInput,
|
||||
gqlFields: objectCreationGqlFields,
|
||||
});
|
||||
|
||||
createdObjectMetadataId = createOneObject.id;
|
||||
|
||||
const {
|
||||
data: { getCoreViews: createdViews },
|
||||
} = await findCoreViews({
|
||||
objectMetadataId: createdObjectMetadataId,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(createdViews).toBeDefined();
|
||||
expect(createdViews.length).toBe(1);
|
||||
const [firstView] = createdViews;
|
||||
|
||||
expect(firstView).toMatchObject<Partial<FlatView>>({
|
||||
objectMetadataId: createdObjectMetadataId,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewFields: createdViewFields },
|
||||
} = await findCoreViewFields({
|
||||
viewId: firstView.id,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(createdViewFields.length).toBe(11);
|
||||
});
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import gql from 'graphql-tag';
|
||||
import { VIEW_FIELD_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants';
|
||||
|
||||
export const findCoreViewFieldsQueryFactory = ({
|
||||
gqlFields = VIEW_FIELD_GQL_FIELDS,
|
||||
viewId,
|
||||
}: {
|
||||
gqlFields?: string;
|
||||
viewId: string;
|
||||
}) => ({
|
||||
query: gql`
|
||||
query GetCoreViewFields($viewId: String!) {
|
||||
getCoreViewFields(viewId: $viewId) {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
viewId,
|
||||
},
|
||||
});
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
||||
import { findCoreViewFieldsQueryFactory } from 'test/integration/metadata/suites/view-field/utils/find-core-view-fields-query-factory.util';
|
||||
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
|
||||
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
|
||||
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
|
||||
|
||||
import { type ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
|
||||
export const findCoreViewFields = async ({
|
||||
viewId,
|
||||
gqlFields,
|
||||
expectToFail,
|
||||
}: {
|
||||
viewId: string;
|
||||
gqlFields?: string;
|
||||
expectToFail?: boolean;
|
||||
}): CommonResponseBody<{
|
||||
getCoreViewFields: ViewFieldEntity[];
|
||||
}> => {
|
||||
const graphqlOperation = findCoreViewFieldsQueryFactory({
|
||||
viewId,
|
||||
gqlFields,
|
||||
});
|
||||
|
||||
const response = await makeMetadataAPIRequest(graphqlOperation);
|
||||
|
||||
if (expectToFail === true) {
|
||||
warnIfNoErrorButExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'View Field search should have failed but did not',
|
||||
});
|
||||
}
|
||||
|
||||
if (expectToFail === false) {
|
||||
warnIfErrorButNotExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'View Field search has failed but should not',
|
||||
});
|
||||
}
|
||||
|
||||
return { data: response.body.data, errors: response.body.errors };
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import gql from 'graphql-tag';
|
||||
import { VIEW_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants';
|
||||
|
||||
export const findCoreViewsQueryFactory = ({
|
||||
gqlFields = VIEW_GQL_FIELDS,
|
||||
objectMetadataId,
|
||||
}: {
|
||||
gqlFields?: string;
|
||||
objectMetadataId?: string;
|
||||
}) => ({
|
||||
query: gql`
|
||||
query GetCoreViews($objectMetadataId: String) {
|
||||
getCoreViews(objectMetadataId: $objectMetadataId) {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: objectMetadataId ? { objectMetadataId } : {},
|
||||
});
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
||||
import { findCoreViewsQueryFactory } from 'test/integration/metadata/suites/view/utils/find-core-views-query-factory.util';
|
||||
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
|
||||
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
|
||||
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
|
||||
|
||||
import { type ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
|
||||
export const findCoreViews = async ({
|
||||
objectMetadataId,
|
||||
gqlFields,
|
||||
expectToFail,
|
||||
}: {
|
||||
objectMetadataId?: string;
|
||||
gqlFields?: string;
|
||||
expectToFail?: boolean;
|
||||
}): CommonResponseBody<{
|
||||
getCoreViews: ViewEntity[];
|
||||
}> => {
|
||||
const graphqlOperation = findCoreViewsQueryFactory({
|
||||
objectMetadataId,
|
||||
gqlFields,
|
||||
});
|
||||
|
||||
const response = await makeMetadataAPIRequest(graphqlOperation);
|
||||
|
||||
if (expectToFail === true) {
|
||||
warnIfNoErrorButExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'View search should have failed but did not',
|
||||
});
|
||||
}
|
||||
|
||||
if (expectToFail === false) {
|
||||
warnIfErrorButNotExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'View search has failed but should not',
|
||||
});
|
||||
}
|
||||
|
||||
return { data: response.body.data, errors: response.body.errors };
|
||||
};
|
||||
Reference in New Issue
Block a user