mirror of
https://github.com/twentyhq/twenty.git
synced 2026-06-11 17:37:18 -04:00
feat(server): convert view to overridable entity
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -30,6 +30,7 @@ export const buildRecordTableWidgetViewSnapshot = (
|
||||
openRecordIn: ViewOpenRecordIn.RECORD_PAGE,
|
||||
visibility: ViewVisibility.UNLISTED,
|
||||
shouldHideEmptyGroups: false,
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
const eligibleFields = objectMetadataItem.fields.filter(
|
||||
|
||||
@@ -33,6 +33,7 @@ export const VIEW_FRAGMENT = gql`
|
||||
calendarLayout
|
||||
visibility
|
||||
createdByUserWorkspaceId
|
||||
isActive
|
||||
viewFields {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import { resolveViewNamePlaceholders } from '@/views/utils/resolveViewNamePlaceh
|
||||
export const viewsSelector = createAtomSelector<ViewWithRelations[]>({
|
||||
key: 'viewsSelector',
|
||||
get: ({ get }) => {
|
||||
const flatViews = get(metadataStoreState, 'views').current as FlatView[];
|
||||
const allFlatViews = get(metadataStoreState, 'views').current as FlatView[];
|
||||
const flatViews = allFlatViews.filter((view) => view.isActive);
|
||||
const flatObjectMetadataItems = get(
|
||||
metadataStoreState,
|
||||
'objectMetadataItems',
|
||||
|
||||
@@ -38,4 +38,5 @@ export type View = {
|
||||
anyFieldFilterValue?: string | null;
|
||||
visibility: ViewVisibility;
|
||||
createdByUserWorkspaceId?: string | null;
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.12.0', 1781114009075)
|
||||
export class ViewOverridableEntityFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE "core"."view" ADD "overrides" jsonb');
|
||||
await queryRunner.query('ALTER TABLE "core"."view" ADD "isActive" boolean NOT NULL DEFAULT true');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE "core"."view" DROP COLUMN "isActive"');
|
||||
await queryRunner.query('ALTER TABLE "core"."view" DROP COLUMN "overrides"');
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ import { EncryptNonSecretApplicationVariableSlowInstanceCommand } from 'src/data
|
||||
import { MigrateAiModelPreferencesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-slow-1799000010000-migrate-ai-model-preferences';
|
||||
import { DropIsCustomFromObjectAndFieldMetadataFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-12/2-12-instance-command-fast-1780579070012-drop-is-custom-from-object-and-field-metadata';
|
||||
import { DropEmailingDomainDriverColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-11/2-11-instance-command-fast-1780926908000-drop-emailing-domain-driver-column';
|
||||
import { ViewOverridableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-12/2-12-instance-command-fast-1781114009075-view-overridable-entity';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -124,4 +125,5 @@ export const INSTANCE_COMMANDS = [
|
||||
EncryptNonSecretApplicationVariableSlowInstanceCommand,
|
||||
DropIsCustomFromObjectAndFieldMetadataFastInstanceCommand,
|
||||
DropEmailingDomainDriverColumnFastInstanceCommand,
|
||||
ViewOverridableEntityFastInstanceCommand,
|
||||
];
|
||||
|
||||
@@ -41,6 +41,8 @@ export const fromViewManifestToUniversalFlatView = ({
|
||||
shouldHideEmptyGroups: viewManifest.shouldHideEmptyGroups ?? false,
|
||||
anyFieldFilterValue: null,
|
||||
createdByUserWorkspaceId: null,
|
||||
isActive: true,
|
||||
universalOverrides: null,
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFilterGroupUniversalIdentifiers: [],
|
||||
|
||||
@@ -271,64 +271,101 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
name: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
type: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
icon: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
name: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
type: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
icon: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
position: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
isCompact: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
openRecordIn: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
kanbanAggregateOperation: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
kanbanAggregateOperationFieldMetadataId: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty:
|
||||
'kanbanAggregateOperationFieldMetadataUniversalIdentifier',
|
||||
isOverridable: true,
|
||||
},
|
||||
anyFieldFilterValue: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
calendarLayout: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
calendarFieldMetadataId: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: 'calendarFieldMetadataUniversalIdentifier',
|
||||
isOverridable: true,
|
||||
},
|
||||
visibility: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
mainGroupByFieldMetadataId: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: 'mainGroupByFieldMetadataUniversalIdentifier',
|
||||
isOverridable: true,
|
||||
},
|
||||
shouldHideEmptyGroups: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
isActive: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: false,
|
||||
},
|
||||
overrides: {
|
||||
toCompare: true,
|
||||
toStringify: true,
|
||||
universalProperty: 'universalOverrides',
|
||||
},
|
||||
objectMetadataId: {
|
||||
toCompare: false,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
ViewKey,
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewVisibility,
|
||||
@@ -14,6 +16,10 @@ import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/
|
||||
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
|
||||
import { computeFlatViewGroupsOnViewCreate } from 'src/engine/metadata-modules/flat-view-group/utils/compute-flat-view-groups-on-view-create.util';
|
||||
import { type CreateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/create-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
} from 'src/engine/metadata-modules/view/exceptions/view.exception';
|
||||
import { type UniversalFlatViewGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-group.type';
|
||||
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
|
||||
|
||||
@@ -39,6 +45,13 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
['id', 'name', 'objectMetadataId'],
|
||||
);
|
||||
|
||||
if (createViewInput.key === ViewKey.INDEX) {
|
||||
throw new ViewException(
|
||||
t`Index views can only be created by the system`,
|
||||
ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
);
|
||||
}
|
||||
|
||||
const createdAt = new Date().toISOString();
|
||||
const viewId = createViewInput.id ?? v4();
|
||||
|
||||
@@ -86,6 +99,8 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
universalIdentifier: createViewInput.universalIdentifier ?? v4(),
|
||||
visibility: createViewInput.visibility ?? ViewVisibility.WORKSPACE,
|
||||
createdByUserWorkspaceId: createdByUserWorkspaceId ?? null,
|
||||
isActive: true,
|
||||
universalOverrides: null,
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewGroupUniversalIdentifiers: [],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
@@ -6,6 +7,7 @@ import {
|
||||
|
||||
import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/flat-view-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
|
||||
import { type DeleteViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/delete-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
@@ -16,9 +18,13 @@ import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-m
|
||||
export const fromDeleteViewInputToFlatViewOrThrow = ({
|
||||
deleteViewInput: rawDeleteViewInput,
|
||||
flatViewMaps,
|
||||
callerApplicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
}: {
|
||||
deleteViewInput: DeleteViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
callerApplicationUniversalIdentifier: string;
|
||||
workspaceCustomApplicationUniversalIdentifier: string;
|
||||
}): UniversalFlatView => {
|
||||
const { id: viewId } = extractAndSanitizeObjectStringFields(
|
||||
rawDeleteViewInput,
|
||||
@@ -37,8 +43,32 @@ export const fromDeleteViewInputToFlatViewOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (existingFlatViewToDelete.key === ViewKey.INDEX) {
|
||||
throw new ViewException(
|
||||
t`Index views cannot be deleted`,
|
||||
ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
);
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const shouldDeactivate = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingFlatViewToDelete.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
if (shouldDeactivate) {
|
||||
return {
|
||||
...existingFlatViewToDelete,
|
||||
isActive: false,
|
||||
updatedAt: now,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...existingFlatViewToDelete,
|
||||
deletedAt: new Date().toISOString(),
|
||||
deletedAt: now,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
@@ -37,5 +38,12 @@ export const fromDestroyViewInputToFlatViewOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (existingFlatViewToDestroy.key === ViewKey.INDEX) {
|
||||
throw new ViewException(
|
||||
t`Index views cannot be destroyed`,
|
||||
ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
);
|
||||
}
|
||||
|
||||
return existingFlatViewToDestroy;
|
||||
};
|
||||
|
||||
@@ -12,8 +12,12 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatViewGroupMaps } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group-maps.type';
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view/constants/flat-view-editable-properties.constant';
|
||||
import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/flat-view-maps.type';
|
||||
import { fromViewOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view/utils/from-view-overrides-to-universal-overrides.util';
|
||||
import { handleFlatViewUpdateSideEffect } from 'src/engine/metadata-modules/flat-view/utils/handle-flat-view-update-side-effect.util';
|
||||
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
|
||||
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
|
||||
import { type UpdateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/update-view.input';
|
||||
import { type ViewOverrides } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
@@ -28,12 +32,16 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
flatViewGroupMaps,
|
||||
flatFieldMetadataMaps,
|
||||
userWorkspaceId,
|
||||
callerApplicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
}: {
|
||||
updateViewInput: UpdateViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
flatViewGroupMaps: FlatViewGroupMaps;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
userWorkspaceId?: string;
|
||||
callerApplicationUniversalIdentifier: string;
|
||||
workspaceCustomApplicationUniversalIdentifier: string;
|
||||
}): {
|
||||
flatViewToUpdate: UniversalFlatView;
|
||||
flatViewGroupsToDelete: UniversalFlatViewGroup[];
|
||||
@@ -57,19 +65,39 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
|
||||
const editableProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateViewInput,
|
||||
FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
const flatViewToUpdate = mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewToUpdate,
|
||||
properties: FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
update: updatedEditableFieldProperties,
|
||||
const shouldOverride = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingFlatViewToUpdate.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
sanitizeOverridableEntityInput({
|
||||
metadataName: 'view',
|
||||
existingFlatEntity: existingFlatViewToUpdate,
|
||||
updatedEditableProperties: editableProperties,
|
||||
shouldOverride,
|
||||
});
|
||||
|
||||
const mergedRecord = mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewToUpdate,
|
||||
properties: [...FLAT_VIEW_EDITABLE_PROPERTIES],
|
||||
update: updatedEditableProperties,
|
||||
});
|
||||
|
||||
const flatViewToUpdate = {
|
||||
...mergedRecord,
|
||||
overrides,
|
||||
} as UniversalFlatView;
|
||||
|
||||
if (
|
||||
updatedEditableFieldProperties.kanbanAggregateOperationFieldMetadataId !==
|
||||
updatedEditableProperties.kanbanAggregateOperationFieldMetadataId !==
|
||||
undefined
|
||||
) {
|
||||
const { kanbanAggregateOperationFieldMetadataUniversalIdentifier } =
|
||||
@@ -77,7 +105,7 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
metadataName: 'view',
|
||||
foreignKeyValues: {
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
flatViewToUpdate.kanbanAggregateOperationFieldMetadataId,
|
||||
mergedRecord.kanbanAggregateOperationFieldMetadataId,
|
||||
},
|
||||
flatEntityMaps: { flatFieldMetadataMaps },
|
||||
});
|
||||
@@ -86,12 +114,12 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
kanbanAggregateOperationFieldMetadataUniversalIdentifier;
|
||||
}
|
||||
|
||||
if (updatedEditableFieldProperties.calendarFieldMetadataId !== undefined) {
|
||||
if (updatedEditableProperties.calendarFieldMetadataId !== undefined) {
|
||||
const { calendarFieldMetadataUniversalIdentifier } =
|
||||
resolveEntityRelationUniversalIdentifiers({
|
||||
metadataName: 'view',
|
||||
foreignKeyValues: {
|
||||
calendarFieldMetadataId: flatViewToUpdate.calendarFieldMetadataId,
|
||||
calendarFieldMetadataId: mergedRecord.calendarFieldMetadataId,
|
||||
},
|
||||
flatEntityMaps: { flatFieldMetadataMaps },
|
||||
});
|
||||
@@ -100,13 +128,12 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
calendarFieldMetadataUniversalIdentifier;
|
||||
}
|
||||
|
||||
if (updatedEditableFieldProperties.mainGroupByFieldMetadataId !== undefined) {
|
||||
if (updatedEditableProperties.mainGroupByFieldMetadataId !== undefined) {
|
||||
const { mainGroupByFieldMetadataUniversalIdentifier } =
|
||||
resolveEntityRelationUniversalIdentifiers({
|
||||
metadataName: 'view',
|
||||
foreignKeyValues: {
|
||||
mainGroupByFieldMetadataId:
|
||||
flatViewToUpdate.mainGroupByFieldMetadataId,
|
||||
mainGroupByFieldMetadataId: mergedRecord.mainGroupByFieldMetadataId,
|
||||
},
|
||||
flatEntityMaps: { flatFieldMetadataMaps },
|
||||
});
|
||||
@@ -115,6 +142,18 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
mainGroupByFieldMetadataUniversalIdentifier;
|
||||
}
|
||||
|
||||
if (isDefined(overrides)) {
|
||||
flatViewToUpdate.universalOverrides = fromViewOverridesToUniversalOverrides(
|
||||
{
|
||||
overrides: overrides as ViewOverrides,
|
||||
fieldMetadataUniversalIdentifierById:
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
flatViewToUpdate.universalOverrides = null;
|
||||
}
|
||||
|
||||
// If changing visibility from WORKSPACE to UNLISTED, ensure createdByUserWorkspaceId is set
|
||||
// This prevents the view from disappearing for the user making the change
|
||||
if (
|
||||
@@ -127,10 +166,15 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
flatViewToUpdate.createdByUserWorkspaceId = userWorkspaceId;
|
||||
}
|
||||
|
||||
const effectiveFlatViewToUpdate = {
|
||||
...mergedRecord,
|
||||
...((overrides as ViewOverrides | null) ?? {}),
|
||||
};
|
||||
|
||||
const { flatViewGroupsToDelete, flatViewGroupsToCreate } =
|
||||
handleFlatViewUpdateSideEffect({
|
||||
fromFlatView: existingFlatViewToUpdate,
|
||||
toFlatView: flatViewToUpdate,
|
||||
toFlatView: effectiveFlatViewToUpdate,
|
||||
flatViewGroupMaps: flatViewGroupMaps,
|
||||
flatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { fromViewOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view/utils/from-view-overrides-to-universal-overrides.util';
|
||||
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
|
||||
|
||||
export const fromViewEntityToFlatView = ({
|
||||
@@ -88,12 +89,23 @@ export const fromViewEntityToFlatView = ({
|
||||
}
|
||||
}
|
||||
|
||||
const universalOverrides = isDefined(viewEntity.overrides)
|
||||
? fromViewOverridesToUniversalOverrides({
|
||||
overrides: viewEntity.overrides,
|
||||
fieldMetadataUniversalIdentifierById: Object.fromEntries(
|
||||
fieldMetadataIdToUniversalIdentifierMap.entries(),
|
||||
),
|
||||
shouldThrowOnMissingIdentifier: false,
|
||||
})
|
||||
: null;
|
||||
|
||||
return {
|
||||
...viewEntityWithoutRelations,
|
||||
createdAt: viewEntity.createdAt.toISOString(),
|
||||
updatedAt: viewEntity.updatedAt.toISOString(),
|
||||
deletedAt: viewEntity.deletedAt?.toISOString() ?? null,
|
||||
universalIdentifier: viewEntityWithoutRelations.universalIdentifier,
|
||||
universalOverrides,
|
||||
viewFieldIds: viewEntity.viewFields.map(({ id }) => id),
|
||||
viewFieldGroupIds: viewEntity.viewFieldGroups?.map(({ id }) => id) ?? [],
|
||||
viewFilterIds: viewEntity.viewFilters.map(({ id }) => id),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { type ViewOverrides } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
|
||||
type UniversalViewOverrides =
|
||||
FormatRecordSerializedRelationProperties<ViewOverrides>;
|
||||
|
||||
const VIEW_OVERRIDES_FIELD_METADATA_FOREIGN_KEYS = [
|
||||
'kanbanAggregateOperationFieldMetadataId',
|
||||
'calendarFieldMetadataId',
|
||||
'mainGroupByFieldMetadataId',
|
||||
] as const;
|
||||
|
||||
type ViewOverridesFieldMetadataForeignKey =
|
||||
(typeof VIEW_OVERRIDES_FIELD_METADATA_FOREIGN_KEYS)[number];
|
||||
|
||||
const toUniversalIdentifierProperty = (
|
||||
foreignKey: ViewOverridesFieldMetadataForeignKey,
|
||||
) =>
|
||||
foreignKey.replace(
|
||||
/Id$/,
|
||||
'UniversalIdentifier',
|
||||
) as keyof UniversalViewOverrides;
|
||||
|
||||
export const fromViewOverridesToUniversalOverrides = ({
|
||||
overrides,
|
||||
fieldMetadataUniversalIdentifierById,
|
||||
shouldThrowOnMissingIdentifier = true,
|
||||
}: {
|
||||
overrides: ViewOverrides;
|
||||
fieldMetadataUniversalIdentifierById: Partial<Record<string, string>>;
|
||||
shouldThrowOnMissingIdentifier?: boolean;
|
||||
}): UniversalViewOverrides => {
|
||||
const {
|
||||
kanbanAggregateOperationFieldMetadataId: _kanban,
|
||||
calendarFieldMetadataId: _calendar,
|
||||
mainGroupByFieldMetadataId: _mainGroupBy,
|
||||
...scalarOverrides
|
||||
} = overrides;
|
||||
|
||||
return VIEW_OVERRIDES_FIELD_METADATA_FOREIGN_KEYS.reduce<UniversalViewOverrides>(
|
||||
(acc, foreignKey) => {
|
||||
const foreignKeyValue = overrides[foreignKey];
|
||||
|
||||
if (foreignKeyValue === undefined) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const universalIdentifierProperty =
|
||||
toUniversalIdentifierProperty(foreignKey);
|
||||
|
||||
if (foreignKeyValue === null) {
|
||||
return { ...acc, [universalIdentifierProperty]: null };
|
||||
}
|
||||
|
||||
const universalIdentifier =
|
||||
fieldMetadataUniversalIdentifierById[foreignKeyValue];
|
||||
|
||||
if (!isDefined(universalIdentifier)) {
|
||||
if (shouldThrowOnMissingIdentifier) {
|
||||
throw new FlatEntityMapsException(
|
||||
`FieldMetadata universal identifier not found for id: ${foreignKeyValue}`,
|
||||
FlatEntityMapsExceptionCode.RELATION_UNIVERSAL_IDENTIFIER_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return { ...acc, [universalIdentifierProperty]: null };
|
||||
}
|
||||
|
||||
return { ...acc, [universalIdentifierProperty]: universalIdentifier };
|
||||
},
|
||||
scalarOverrides,
|
||||
);
|
||||
};
|
||||
@@ -735,6 +735,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
universalIdentifier: v4(),
|
||||
visibility: ViewVisibility.WORKSPACE,
|
||||
createdByUserWorkspaceId: null,
|
||||
isActive: true,
|
||||
universalOverrides: null,
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFieldGroupUniversalIdentifiers: [],
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
|
||||
@@ -42,6 +42,8 @@ export const computeFlatRecordPageFieldsViewToCreate = ({
|
||||
universalIdentifier: v4(),
|
||||
visibility: ViewVisibility.WORKSPACE,
|
||||
createdByUserWorkspaceId: null,
|
||||
isActive: true,
|
||||
universalOverrides: null,
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFieldGroupUniversalIdentifiers: [],
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import {
|
||||
Field,
|
||||
HideField,
|
||||
ObjectType,
|
||||
registerEnumType,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
import {
|
||||
@@ -12,6 +17,7 @@ import {
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto';
|
||||
import { type ViewOverrides } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
|
||||
import { ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto';
|
||||
import { ViewFilterDTO } from 'src/engine/metadata-modules/view-filter/dtos/view-filter.dto';
|
||||
@@ -117,4 +123,10 @@ export class ViewDTO {
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
createdByUserWorkspaceId?: string | null;
|
||||
|
||||
@Field(() => Boolean, { nullable: false })
|
||||
isActive: boolean;
|
||||
|
||||
@HideField()
|
||||
overrides?: ViewOverrides | null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from 'typeorm';
|
||||
import {
|
||||
AggregateOperations,
|
||||
type SerializedRelation,
|
||||
ViewCalendarLayout,
|
||||
ViewKey,
|
||||
ViewOpenRecordIn,
|
||||
@@ -30,7 +31,24 @@ import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-g
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
import { OverridableEntity } from 'src/engine/workspace-manager/types/overridable-entity';
|
||||
|
||||
export type ViewOverrides = {
|
||||
name?: string;
|
||||
type?: ViewType;
|
||||
icon?: string;
|
||||
position?: number;
|
||||
isCompact?: boolean;
|
||||
openRecordIn?: ViewOpenRecordIn;
|
||||
kanbanAggregateOperation?: AggregateOperations | null;
|
||||
kanbanAggregateOperationFieldMetadataId?: SerializedRelation | null;
|
||||
anyFieldFilterValue?: string | null;
|
||||
calendarLayout?: ViewCalendarLayout | null;
|
||||
calendarFieldMetadataId?: SerializedRelation | null;
|
||||
visibility?: ViewVisibility;
|
||||
mainGroupByFieldMetadataId?: SerializedRelation | null;
|
||||
shouldHideEmptyGroups?: boolean;
|
||||
};
|
||||
|
||||
// We could refactor this type to be dynamic to view type
|
||||
@Entity({ name: 'view', schema: 'core' })
|
||||
@@ -49,7 +67,10 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti
|
||||
'CHK_VIEW_CALENDAR_INTEGRITY',
|
||||
`("type" != 'CALENDAR' OR ("calendarLayout" IS NOT NULL AND "calendarFieldMetadataId" IS NOT NULL))`,
|
||||
)
|
||||
export class ViewEntity extends SyncableEntity implements Required<ViewEntity> {
|
||||
export class ViewEntity
|
||||
extends OverridableEntity<ViewOverrides>
|
||||
implements Required<ViewEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { fromCreateViewInputToFlatViewToCreate } from 'src/engine/metadata-modul
|
||||
import { fromDeleteViewInputToFlatViewOrThrow } from 'src/engine/metadata-modules/flat-view/utils/from-delete-view-input-to-flat-view-or-throw.util';
|
||||
import { fromDestroyViewInputToFlatViewOrThrow } from 'src/engine/metadata-modules/flat-view/utils/from-destroy-view-input-to-flat-view-or-throw.util';
|
||||
import { fromUpdateViewInputToFlatViewToUpdateOrThrow } from 'src/engine/metadata-modules/flat-view/utils/from-update-view-input-to-flat-view-to-update-or-throw.util';
|
||||
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
|
||||
import { fromFlatViewFieldGroupToViewFieldGroupDto } from 'src/engine/metadata-modules/view-field-group/utils/from-flat-view-field-group-to-view-field-group-dto.util';
|
||||
import { fromFlatViewFieldToViewFieldDto } from 'src/engine/metadata-modules/view-field/utils/from-flat-view-field-to-view-field-dto.util';
|
||||
import { fromFlatViewFilterGroupToViewFilterGroupDto } from 'src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util';
|
||||
@@ -166,6 +167,10 @@ export class ViewService {
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
userWorkspaceId,
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
@@ -239,6 +244,10 @@ export class ViewService {
|
||||
fromDeleteViewInputToFlatViewOrThrow({
|
||||
deleteViewInput,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
@@ -313,14 +322,29 @@ export class ViewService {
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
});
|
||||
|
||||
const shouldDeactivate = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingFlatView.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
view: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [flatViewFromDestroyInput],
|
||||
flatEntityToUpdate: [],
|
||||
flatEntityToDelete: shouldDeactivate
|
||||
? []
|
||||
: [flatViewFromDestroyInput],
|
||||
flatEntityToUpdate: shouldDeactivate
|
||||
? [{ ...existingFlatView, isActive: false, updatedAt: now }]
|
||||
: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
@@ -337,9 +361,17 @@ export class ViewService {
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldDeactivate) {
|
||||
return fromFlatViewToViewDto({
|
||||
...existingFlatView,
|
||||
isActive: false,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
return fromFlatViewToViewDto({
|
||||
...existingFlatView,
|
||||
deletedAt: new Date().toISOString(),
|
||||
deletedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-
|
||||
import { type ViewDTO } from 'src/engine/metadata-modules/view/dtos/view.dto';
|
||||
|
||||
export const fromFlatViewToViewDto = (flatView: FlatView): ViewDTO => {
|
||||
const { createdAt, updatedAt, deletedAt, ...rest } = flatView;
|
||||
const { createdAt, updatedAt, deletedAt, overrides, ...rest } = flatView;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
...(overrides ?? {}),
|
||||
createdAt: new Date(createdAt),
|
||||
updatedAt: new Date(updatedAt),
|
||||
deletedAt: deletedAt ? new Date(deletedAt) : null,
|
||||
|
||||
@@ -142,6 +142,9 @@ export const createStandardViewFlatMetadata = <
|
||||
anyFieldFilterValue: null,
|
||||
visibility: ViewVisibility.WORKSPACE,
|
||||
createdByUserWorkspaceId: null,
|
||||
isActive: true,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
viewFieldIds: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFieldGroupIds: [],
|
||||
|
||||
@@ -12,7 +12,9 @@ export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
|
||||
settings: 'settings',
|
||||
},
|
||||
objectMetadata: {},
|
||||
view: {},
|
||||
view: {
|
||||
overrides: 'overrides',
|
||||
},
|
||||
viewField: {
|
||||
overrides: 'overrides',
|
||||
},
|
||||
|
||||
@@ -64,6 +64,8 @@ type Assertions = [
|
||||
| 'anyFieldFilterValue'
|
||||
| 'visibility'
|
||||
| 'createdByUserWorkspaceId'
|
||||
| 'isActive'
|
||||
| 'universalOverrides'
|
||||
>
|
||||
>,
|
||||
];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
FlatCreateViewAction,
|
||||
UniversalCreateViewAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/types/workspace-migration-view-action.type';
|
||||
import { fromUniversalOverridesToViewOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/utils/from-universal-overrides-to-view-overrides.util';
|
||||
import {
|
||||
WorkspaceMigrationActionRunnerArgs,
|
||||
WorkspaceMigrationActionRunnerContext,
|
||||
@@ -41,6 +43,13 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
universalForeignKeyValues: action.flatEntity,
|
||||
});
|
||||
|
||||
const overrides = isDefined(action.flatEntity.universalOverrides)
|
||||
? fromUniversalOverridesToViewOverrides({
|
||||
universalOverrides: action.flatEntity.universalOverrides,
|
||||
flatFieldMetadataMaps: allFlatEntityMaps.flatFieldMetadataMaps,
|
||||
})
|
||||
: null;
|
||||
|
||||
const emptyUniversalForeignKeyAggregators =
|
||||
getUniversalFlatEntityEmptyForeignKeyAggregators({
|
||||
metadataName: 'view',
|
||||
@@ -54,6 +63,7 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
kanbanAggregateOperationFieldMetadataId,
|
||||
mainGroupByFieldMetadataId,
|
||||
objectMetadataId,
|
||||
overrides,
|
||||
id: action.id ?? v4(),
|
||||
applicationId: flatApplication.id,
|
||||
workspaceId,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
FlatUpdateViewAction,
|
||||
UniversalUpdateViewAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/types/workspace-migration-view-action.type';
|
||||
import { fromUniversalOverridesToViewOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/utils/from-universal-overrides-to-view-overrides.util';
|
||||
import {
|
||||
WorkspaceMigrationActionRunnerArgs,
|
||||
WorkspaceMigrationActionRunnerContext,
|
||||
@@ -33,11 +34,25 @@ export class UpdateViewActionHandlerService extends WorkspaceMigrationRunnerActi
|
||||
universalIdentifier: action.universalIdentifier,
|
||||
});
|
||||
|
||||
const update = resolveUniversalUpdateRelationIdentifiersToIds({
|
||||
metadataName: 'view',
|
||||
universalUpdate: action.update,
|
||||
allFlatEntityMaps,
|
||||
});
|
||||
const { universalOverrides, ...updateWithResolvedForeignKeys } =
|
||||
resolveUniversalUpdateRelationIdentifiersToIds({
|
||||
metadataName: 'view',
|
||||
universalUpdate: action.update,
|
||||
allFlatEntityMaps,
|
||||
});
|
||||
|
||||
const update =
|
||||
universalOverrides === undefined
|
||||
? updateWithResolvedForeignKeys
|
||||
: universalOverrides === null
|
||||
? { ...updateWithResolvedForeignKeys, overrides: null }
|
||||
: {
|
||||
...updateWithResolvedForeignKeys,
|
||||
overrides: fromUniversalOverridesToViewOverrides({
|
||||
universalOverrides,
|
||||
flatFieldMetadataMaps: allFlatEntityMaps.flatFieldMetadataMaps,
|
||||
}),
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'update',
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type ViewOverrides } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
|
||||
type UniversalViewOverrides =
|
||||
FormatRecordSerializedRelationProperties<ViewOverrides>;
|
||||
|
||||
const VIEW_OVERRIDES_UNIVERSAL_FIELD_METADATA_PROPERTIES = [
|
||||
'kanbanAggregateOperationFieldMetadataUniversalIdentifier',
|
||||
'calendarFieldMetadataUniversalIdentifier',
|
||||
'mainGroupByFieldMetadataUniversalIdentifier',
|
||||
] as const;
|
||||
|
||||
type ViewOverridesUniversalFieldMetadataProperty =
|
||||
(typeof VIEW_OVERRIDES_UNIVERSAL_FIELD_METADATA_PROPERTIES)[number];
|
||||
|
||||
const toForeignKeyProperty = (
|
||||
universalProperty: ViewOverridesUniversalFieldMetadataProperty,
|
||||
) =>
|
||||
universalProperty.replace(
|
||||
/UniversalIdentifier$/,
|
||||
'Id',
|
||||
) as keyof ViewOverrides;
|
||||
|
||||
export const fromUniversalOverridesToViewOverrides = ({
|
||||
universalOverrides,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
universalOverrides: UniversalViewOverrides;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): ViewOverrides => {
|
||||
const {
|
||||
kanbanAggregateOperationFieldMetadataUniversalIdentifier: _kanban,
|
||||
calendarFieldMetadataUniversalIdentifier: _calendar,
|
||||
mainGroupByFieldMetadataUniversalIdentifier: _mainGroupBy,
|
||||
...scalarOverrides
|
||||
} = universalOverrides;
|
||||
|
||||
return VIEW_OVERRIDES_UNIVERSAL_FIELD_METADATA_PROPERTIES.reduce<ViewOverrides>(
|
||||
(acc, universalProperty) => {
|
||||
const universalIdentifier = universalOverrides[universalProperty];
|
||||
|
||||
if (universalIdentifier === undefined) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const foreignKeyProperty = toForeignKeyProperty(universalProperty);
|
||||
|
||||
if (universalIdentifier === null) {
|
||||
return { ...acc, [foreignKeyProperty]: null };
|
||||
}
|
||||
|
||||
const flatFieldMetadata =
|
||||
findFlatEntityByUniversalIdentifier<FlatFieldMetadata>({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
universalIdentifier,
|
||||
});
|
||||
|
||||
return { ...acc, [foreignKeyProperty]: flatFieldMetadata?.id ?? null };
|
||||
},
|
||||
scalarOverrides,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user