Fix and refactor all metadata relation (#17978)
# Introduction The initial motivation was that in the workspace migration create action some universal foreign key aggregators weren't correctly deleted before returned due to constant missconfiguration <img width="2300" height="972" alt="image" src="https://github.com/user-attachments/assets/9401eb02-2bb2-4e69-9c5f-9a354ff61079" /> It also meant that under the hood some optimistic behavior wasn't correctly rendered for some aggregators ## Solution Refactored the `ALL_METADATA_RELATIONS` as follows: This way we can infer the FK and transpile it to a universalFK, also the aggregators are one to one instead of one versus all available Making the only manual configuration to be defined the `foreignKey` and `inverseOneToManyProperty` ``` ┌──────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ │ ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY│ │ ALL_ONE_TO_MANY_METADATA_RELATIONS │ │──────────────────────────────────────│ │─────────────────────────────────────────────│ │ Derived from: Entity types │ │ Derived from: Entity types │ │ │ │ │ │ Provides: │ │ Provides: │ │ • foreignKey │ │ • metadataName │ │ │ │ • flatEntityForeignKeyAggregator │ │ Standalone low-level primitive │ │ • universalFlatEntityForeignKeyAggregator │ └──────────────┬───────────────────────┘ └──────────────┬──────────────────────────────┘ │ │ │ foreignKey type + │ inverseOneToManyProperty │ universalForeignKey derivation │ keys (type constraint) │ │ ▼ ▼ ┌───────────────────────────────────────────────────────────────┐ │ ALL_MANY_TO_ONE_METADATA_RELATIONS │ │───────────────────────────────────────────────────────────────│ │ Derived from: │ │ • Entity types (metadataName, isNullable) │ │ • ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY (FK → universalFK) │ │ • ALL_ONE_TO_MANY_METADATA_RELATIONS (inverse keys) │ │ │ │ Provides: │ │ • metadataName │ │ • foreignKey (replicated from FK constant) │ │ • inverseOneToManyProperty │ │ • isNullable │ │ • universalForeignKey │ └──────────────────────────┬────────────────────────────────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────────┐ ┌────────────────┐ ┌──────────────────────┐ │ Type consumers │ │ Atomic utils │ │ Optimistic utils │ │───────────────────│ │────────────────│ │──────────────────────│ │ • JoinColumn │ │ • resolve-* │ │ • add/delete flat │ │ • RelatedNames │ │ • get-* │ │ entity maps │ │ • UniversalFlat │ │ │ │ • add/delete │ │ EntityFrom │ │ │ │ universal flat │ │ │ │ │ │ entity maps │ └───────────────────┘ └────────────────┘ │ │ │ (bridge via │ │ inverseOneToMany │ │ Property → │ │ ONE_TO_MANY for │ │ aggregator lookup) │ └──────────────────────┘ ``` ### Previously ``` ┌─────────────────────────────────────────────────────────────────────┐ │ ALL_METADATA_RELATIONS │ │─────────────────────────────────────────────────────────────────────│ │ Derived from: Entity types │ │ │ │ Structure: { [metadataName]: { manyToOne: {...}, oneToMany: {...},│ │ serializedRelations?: {...} } } │ │ │ │ manyToOne provides: │ │ • metadataName │ │ • foreignKey │ │ • flatEntityForeignKeyAggregator (nullable, often wrong/null) │ │ • isNullable │ │ │ │ oneToMany provides: │ │ • metadataName │ │ │ │ Monolithic single source of truth │ └──────────────────────────┬──────────────────────────────────────────┘ │ │ manyToOne entries transformed via │ ToUniversalMetadataManyToOneRelationConfiguration │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ ALL_UNIVERSAL_METADATA_RELATIONS │ │─────────────────────────────────────────────────────────────────────│ │ Derived from: ALL_METADATA_RELATIONS (type-level transform) │ │ │ │ Structure: { [metadataName]: { manyToOne: {...}, oneToMany: {...} │ │ } } │ │ │ │ manyToOne provides: │ │ • metadataName │ │ • foreignKey │ │ • universalForeignKey (derived: FK → replace Id → UniversalId) │ │ • universalFlatEntityForeignKeyAggregator (derived from │ │ flatEntityForeignKeyAggregator → replace Ids → UniversalIds) │ │ • isNullable │ │ │ │ oneToMany: passthrough from ALL_METADATA_RELATIONS │ │ │ │ Duplicated monolith with universal key transforms │ └──────────────────────────┬──────────────────────────────────────────┘ │ ┌──────────────────┼──────────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌────────────────────┐ ┌──────────────────────┐ │ Type consumers│ │ Atomic utils │ │ Optimistic utils │ │───────────────│ │────────────────────│ │──────────────────────│ │ • JoinColumn │ │ • resolve-entity- │ │ • add/delete flat │ │ • RelatedNames│ │ relation-univ-id │ │ entity maps │ │ • Universal │ │ (ALL_METADATA_ │ │ (ALL_METADATA_ │ │ FlatEntity │ │ RELATIONS │ │ RELATIONS │ │ From │ │ .manyToOne) │ │ .manyToOne) │ │ │ │ │ │ │ │ Mixed usage │ │ • resolve-univ- │ │ • add/delete univ │ │ of both │ │ relation-ids │ │ flat entity maps │ │ constants │ │ (ALL_UNIVERSAL_ │ │ (ALL_UNIVERSAL_ │ │ │ │ METADATA_REL │ │ METADATA_REL │ │ │ │ .manyToOne) │ │ .manyToOne) │ │ │ │ │ │ │ │ │ │ • resolve-univ- │ │ universalFlatEntity │ │ │ │ update-rel-ids │ │ ForeignKeyAggregator │ │ │ │ (ALL_UNIVERSAL_ │ │ read directly from │ │ │ │ METADATA_REL │ │ the constant │ │ │ │ .manyToOne) │ │ │ │ │ │ │ │ │ │ │ │ • regex hack: │ │ │ │ │ │ foreignKey │ │ │ │ │ │ .replace(/Id$/, │ │ │ │ │ │ 'UniversalId') │ │ │ └───────────────┘ └────────────────────┘ └──────────────────────┘ ```
This commit is contained in:
+2
-2
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with children first (most manyToOne relations first) 1`] = `
|
||||
[
|
||||
@@ -23,7 +23,7 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
|
||||
"webhook",
|
||||
"view",
|
||||
"fieldMetadata",
|
||||
"role",
|
||||
"objectMetadata",
|
||||
"role",
|
||||
]
|
||||
`;
|
||||
|
||||
+36
-18
@@ -1,7 +1,8 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
@@ -45,29 +46,46 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
selfFlatEntityMaps.universalIdentifierById[flatEntity.id] =
|
||||
flatEntity.universalIdentifier;
|
||||
|
||||
const idBasedManyToOneRelations = Object.values(
|
||||
ALL_METADATA_RELATIONS[metadataName].manyToOne,
|
||||
) as Array<{
|
||||
metadataName: AllMetadataName;
|
||||
flatEntityForeignKeyAggregator: keyof MetadataFlatEntity<AllMetadataName>;
|
||||
foreignKey: keyof MetadataFlatEntity<T>;
|
||||
} | null>;
|
||||
const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName];
|
||||
|
||||
for (const idBasedRelation of idBasedManyToOneRelations) {
|
||||
if (!isDefined(idBasedRelation)) {
|
||||
for (const relationPropertyName of Object.keys(manyToOneRelations)) {
|
||||
const relation = manyToOneRelations[
|
||||
relationPropertyName as keyof typeof manyToOneRelations
|
||||
] as {
|
||||
metadataName: AllMetadataName;
|
||||
foreignKey: string;
|
||||
inverseOneToManyProperty: string | null;
|
||||
} | null;
|
||||
|
||||
if (!isDefined(relation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {
|
||||
metadataName: relatedMetadataName,
|
||||
flatEntityForeignKeyAggregator,
|
||||
foreignKey,
|
||||
} = idBasedRelation;
|
||||
inverseOneToManyProperty,
|
||||
} = relation;
|
||||
|
||||
if (!isDefined(flatEntityForeignKeyAggregator)) {
|
||||
if (!isDefined(inverseOneToManyProperty)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const oneToManyRelations =
|
||||
ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName];
|
||||
|
||||
const inverseRelation = oneToManyRelations[
|
||||
inverseOneToManyProperty as keyof typeof oneToManyRelations
|
||||
] as {
|
||||
flatEntityForeignKeyAggregator: string;
|
||||
} | null;
|
||||
|
||||
if (!isDefined(inverseRelation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { flatEntityForeignKeyAggregator } = inverseRelation;
|
||||
|
||||
const relatedFlatEntityMapsKey =
|
||||
getMetadataFlatEntityMapsKey(relatedMetadataName);
|
||||
|
||||
@@ -75,9 +93,9 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
relatedFlatEntityMapsKey as MetadataRelatedFlatEntityMapsKeys<T>
|
||||
] as FlatEntityMaps<MetadataFlatEntity<typeof relatedMetadataName>>;
|
||||
|
||||
const flatEntityRelatedEntityForeignKeyValue = flatEntity[foreignKey] as
|
||||
| string
|
||||
| undefined;
|
||||
const flatEntityRelatedEntityForeignKeyValue = (
|
||||
flatEntity as unknown as Record<string, string | undefined>
|
||||
)[foreignKey];
|
||||
|
||||
if (!isDefined(flatEntityRelatedEntityForeignKeyValue)) {
|
||||
continue;
|
||||
@@ -103,9 +121,9 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
|
||||
const updatedRelatedEntity = {
|
||||
...relatedFlatEntity,
|
||||
[flatEntityForeignKeyAggregator]: [
|
||||
...(relatedFlatEntity[
|
||||
...((relatedFlatEntity as unknown as Record<string, string[]>)[
|
||||
flatEntityForeignKeyAggregator
|
||||
] as unknown as string[]),
|
||||
] ?? []),
|
||||
flatEntity.id,
|
||||
],
|
||||
};
|
||||
|
||||
+36
-18
@@ -1,7 +1,8 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
@@ -38,29 +39,46 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
|
||||
},
|
||||
);
|
||||
|
||||
const idBasedManyToOneRelations = Object.values(
|
||||
ALL_METADATA_RELATIONS[metadataName].manyToOne,
|
||||
) as Array<{
|
||||
metadataName: AllMetadataName;
|
||||
flatEntityForeignKeyAggregator: keyof MetadataFlatEntity<AllMetadataName>;
|
||||
foreignKey: keyof MetadataFlatEntity<T>;
|
||||
} | null>;
|
||||
const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName];
|
||||
|
||||
for (const idBasedRelation of idBasedManyToOneRelations) {
|
||||
if (!isDefined(idBasedRelation)) {
|
||||
for (const relationPropertyName of Object.keys(manyToOneRelations)) {
|
||||
const relation = manyToOneRelations[
|
||||
relationPropertyName as keyof typeof manyToOneRelations
|
||||
] as {
|
||||
metadataName: AllMetadataName;
|
||||
foreignKey: string;
|
||||
inverseOneToManyProperty: string | null;
|
||||
} | null;
|
||||
|
||||
if (!isDefined(relation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {
|
||||
metadataName: relatedMetadataName,
|
||||
flatEntityForeignKeyAggregator,
|
||||
foreignKey,
|
||||
} = idBasedRelation;
|
||||
inverseOneToManyProperty,
|
||||
} = relation;
|
||||
|
||||
if (!isDefined(flatEntityForeignKeyAggregator)) {
|
||||
if (!isDefined(inverseOneToManyProperty)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const oneToManyRelations =
|
||||
ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName];
|
||||
|
||||
const inverseRelation = oneToManyRelations[
|
||||
inverseOneToManyProperty as keyof typeof oneToManyRelations
|
||||
] as {
|
||||
flatEntityForeignKeyAggregator: string;
|
||||
} | null;
|
||||
|
||||
if (!isDefined(inverseRelation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { flatEntityForeignKeyAggregator } = inverseRelation;
|
||||
|
||||
const relatedFlatEntityMapsKey =
|
||||
getMetadataFlatEntityMapsKey(relatedMetadataName);
|
||||
|
||||
@@ -68,9 +86,9 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
|
||||
relatedFlatEntityMapsKey as MetadataRelatedFlatEntityMapsKeys<T>
|
||||
] as FlatEntityMaps<MetadataFlatEntity<typeof relatedMetadataName>>;
|
||||
|
||||
const flatEntityRelatedEntityForeignKeyValue = flatEntity[foreignKey] as
|
||||
| string
|
||||
| undefined;
|
||||
const flatEntityRelatedEntityForeignKeyValue = (
|
||||
flatEntity as unknown as Record<string, string | undefined>
|
||||
)[foreignKey];
|
||||
|
||||
if (!isDefined(flatEntityRelatedEntityForeignKeyValue)) {
|
||||
continue;
|
||||
@@ -100,9 +118,9 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
|
||||
const updatedRelatedEntity = {
|
||||
...relatedFlatEntity,
|
||||
[flatEntityForeignKeyAggregator]: (
|
||||
relatedFlatEntity[
|
||||
(relatedFlatEntity as unknown as Record<string, string[]>)[
|
||||
flatEntityForeignKeyAggregator
|
||||
] as unknown as string[]
|
||||
] ?? []
|
||||
).filter((id) => id !== flatEntity.id),
|
||||
};
|
||||
|
||||
|
||||
+6
-7
@@ -1,17 +1,16 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant';
|
||||
|
||||
export const getMetadataEntityRelationProperties = <T extends AllMetadataName>(
|
||||
metadataName: T,
|
||||
) => {
|
||||
const relationProperties = ALL_METADATA_RELATIONS[metadataName];
|
||||
|
||||
return [
|
||||
...Object.keys(relationProperties.manyToOne),
|
||||
...Object.keys(relationProperties.oneToMany),
|
||||
...Object.keys(ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]),
|
||||
...Object.keys(ALL_ONE_TO_MANY_METADATA_RELATIONS[metadataName]),
|
||||
] as (
|
||||
| keyof (typeof ALL_METADATA_RELATIONS)[T]['manyToOne']
|
||||
| keyof (typeof ALL_METADATA_RELATIONS)[T]['oneToMany']
|
||||
| keyof (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]
|
||||
| keyof (typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[T]
|
||||
)[];
|
||||
};
|
||||
|
||||
+2
-4
@@ -1,14 +1,12 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import { type MetadataManyToOneRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type';
|
||||
|
||||
export const getMetadataManyToOneRelatedNames = <T extends AllMetadataName>(
|
||||
metadataName: T,
|
||||
): MetadataManyToOneRelatedMetadataNames<T>[] => {
|
||||
const relations = ALL_METADATA_RELATIONS[metadataName];
|
||||
|
||||
return Object.values(relations.manyToOne)
|
||||
return Object.values(ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName])
|
||||
.filter((relation) => relation !== null)
|
||||
.map(
|
||||
(relation) => relation.metadataName,
|
||||
|
||||
+2
-4
@@ -1,14 +1,12 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant';
|
||||
import { type MetadataOneToManyRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type';
|
||||
|
||||
export const getMetadataOneToManyRelatedNames = <T extends AllMetadataName>(
|
||||
metadataName: T,
|
||||
): MetadataOneToManyRelatedMetadataNames<T>[] => {
|
||||
const relations = ALL_METADATA_RELATIONS[metadataName];
|
||||
|
||||
return Object.values(relations.oneToMany)
|
||||
return Object.values(ALL_ONE_TO_MANY_METADATA_RELATIONS[metadataName])
|
||||
.filter((relation) => relation !== null)
|
||||
.map(
|
||||
(relation) => relation.metadataName,
|
||||
|
||||
+4
-8
@@ -1,15 +1,11 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_METADATA_SERIALIZED_RELATION } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant';
|
||||
|
||||
export const getMetadataSerializedRelationNames = (
|
||||
metadataName: AllMetadataName,
|
||||
): AllMetadataName[] => {
|
||||
const relations = ALL_METADATA_RELATIONS[metadataName];
|
||||
|
||||
if (!('serializedRelations' in relations)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.keys(relations.serializedRelations) as AllMetadataName[];
|
||||
return Object.keys(
|
||||
ALL_METADATA_SERIALIZED_RELATION[metadataName],
|
||||
) as AllMetadataName[];
|
||||
};
|
||||
|
||||
+18
-24
@@ -1,25 +1,19 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type RemoveSuffix } from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
ALL_METADATA_RELATIONS,
|
||||
type MetadataManyToOneRelationConfiguration,
|
||||
} from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type';
|
||||
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
|
||||
import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type';
|
||||
import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
|
||||
type ManyToOneConfig<T extends AllMetadataName> =
|
||||
(typeof ALL_METADATA_RELATIONS)[T]['manyToOne'];
|
||||
(typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T];
|
||||
|
||||
type TargetMetadataNamesForForeignKeys<
|
||||
T extends AllMetadataName,
|
||||
@@ -49,9 +43,10 @@ type ResolvedUniversalIdentifiers<
|
||||
> = {
|
||||
[K in keyof ManyToOneConfig<T> as ManyToOneConfig<T>[K] extends {
|
||||
foreignKey: infer FK extends string;
|
||||
universalForeignKey: infer UFK extends string;
|
||||
}
|
||||
? FK extends TProvidedKeys
|
||||
? `${RemoveSuffix<FK, 'Id'>}UniversalIdentifier`
|
||||
? UFK
|
||||
: never
|
||||
: never]: ManyToOneConfig<T>[K] extends { isNullable: true }
|
||||
? string | null
|
||||
@@ -73,15 +68,19 @@ export const resolveEntityRelationUniversalIdentifiers = <
|
||||
foreignKeyValues: Record<TProvidedKeys, string | null | undefined>;
|
||||
flatEntityMaps: RequiredFlatEntityMapsForForeignKeys<T, TProvidedKeys>;
|
||||
}): ResolvedUniversalIdentifiers<T, TProvidedKeys> => {
|
||||
const relations = ALL_METADATA_RELATIONS[metadataName].manyToOne;
|
||||
const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName];
|
||||
const result: Record<string, string | null> = {};
|
||||
|
||||
for (const relation of Object.values(
|
||||
relations,
|
||||
) as MetadataManyToOneRelationConfiguration<
|
||||
T,
|
||||
ExtractEntityManyToOneEntityRelationProperties<MetadataEntity<T>>
|
||||
>[]) {
|
||||
for (const relationPropertyName of Object.keys(relationEntries)) {
|
||||
const relation = relationEntries[
|
||||
relationPropertyName as keyof typeof relationEntries
|
||||
] as {
|
||||
foreignKey: string;
|
||||
metadataName: AllMetadataName;
|
||||
isNullable: boolean;
|
||||
universalForeignKey: string;
|
||||
} | null;
|
||||
|
||||
if (!isDefined(relation)) {
|
||||
continue;
|
||||
}
|
||||
@@ -90,6 +89,7 @@ export const resolveEntityRelationUniversalIdentifiers = <
|
||||
foreignKey,
|
||||
metadataName: targetMetadataName,
|
||||
isNullable,
|
||||
universalForeignKey,
|
||||
} = relation;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(foreignKeyValues, foreignKey)) {
|
||||
@@ -104,14 +104,8 @@ export const resolveEntityRelationUniversalIdentifiers = <
|
||||
) as keyof RequiredFlatEntityMapsForForeignKeys<T, TProvidedKeys>;
|
||||
const targetFlatEntityMaps = flatEntityMaps[mapsKey];
|
||||
|
||||
// TODO refactor using the new ALL_METADATA_UNIVERSAL_RELATION afterwards
|
||||
const universalIdentifierKey = foreignKey.replace(
|
||||
/Id$/,
|
||||
'UniversalIdentifier',
|
||||
);
|
||||
|
||||
if (isNullable && !isDefined(foreignKeyValue)) {
|
||||
result[universalIdentifierKey] = null;
|
||||
result[universalForeignKey] = null;
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -126,7 +120,7 @@ export const resolveEntityRelationUniversalIdentifiers = <
|
||||
);
|
||||
}
|
||||
|
||||
result[universalIdentifierKey] = resolvedUniversalIdentifier;
|
||||
result[universalForeignKey] = resolvedUniversalIdentifier;
|
||||
}
|
||||
|
||||
return result as ResolvedUniversalIdentifiers<T, TProvidedKeys>;
|
||||
|
||||
Reference in New Issue
Block a user