Resolve involved application ids from API metadata (#18221)
# Introduction Resolving each create|updated|deleted entities related entities application through their universal foreingKey ( silently failing if not found leaving the validator handling that ) In order to compute all the required application in the dependency flat entity maps ## Tested Creating a field on a custom local twenty-apps installed on the workspace + view field ## Out of scope - updated snapshot - update graphql generated front
This commit is contained in:
@@ -1544,6 +1544,7 @@ export type Field = {
|
||||
settings?: Maybe<Scalars['JSON']>;
|
||||
standardOverrides?: Maybe<StandardOverrides>;
|
||||
type: FieldMetadataType;
|
||||
universalIdentifier: Scalars['UUID'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
@@ -1576,6 +1577,7 @@ export type FieldFilter = {
|
||||
isSystem?: InputMaybe<BooleanFieldComparison>;
|
||||
isUIReadOnly?: InputMaybe<BooleanFieldComparison>;
|
||||
or?: InputMaybe<Array<FieldFilter>>;
|
||||
universalIdentifier?: InputMaybe<UuidFilterComparison>;
|
||||
};
|
||||
|
||||
/** Type of the field */
|
||||
@@ -3351,6 +3353,7 @@ export type Object = {
|
||||
nameSingular: Scalars['String'];
|
||||
shortcut?: Maybe<Scalars['String']>;
|
||||
standardOverrides?: Maybe<ObjectStandardOverrides>;
|
||||
universalIdentifier: Scalars['UUID'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
@@ -3400,6 +3403,7 @@ export type ObjectFilter = {
|
||||
isSystem?: InputMaybe<BooleanFieldComparison>;
|
||||
isUIReadOnly?: InputMaybe<BooleanFieldComparison>;
|
||||
or?: InputMaybe<Array<ObjectFilter>>;
|
||||
universalIdentifier?: InputMaybe<UuidFilterComparison>;
|
||||
};
|
||||
|
||||
export type ObjectIndexMetadatasConnection = {
|
||||
@@ -4597,6 +4601,7 @@ export type UpdateFieldInput = {
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
options?: InputMaybe<Scalars['JSON']>;
|
||||
settings?: InputMaybe<Scalars['JSON']>;
|
||||
universalIdentifier?: InputMaybe<Scalars['UUID']>;
|
||||
};
|
||||
|
||||
export type UpdateFrontComponentInput = {
|
||||
|
||||
+125
-23
@@ -6,8 +6,10 @@ import {
|
||||
} from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatApplicationCacheMaps } from 'src/engine/core-modules/application/types/flat-application-cache-maps.type';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { MetadataEventEmitter } from 'src/engine/metadata-event-emitter/metadata-event-emitter';
|
||||
import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
@@ -68,6 +70,122 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
this.isDebugEnabled = logLevels.includes('debug');
|
||||
}
|
||||
|
||||
private computeAllInvolvedApplicationIds({
|
||||
allFlatEntityOperationByMetadataName,
|
||||
flatApplicationMaps,
|
||||
applicationUniversalIdentifier,
|
||||
allRelatedFlatEntityMaps,
|
||||
}: Pick<
|
||||
ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs,
|
||||
'allFlatEntityOperationByMetadataName' | 'applicationUniversalIdentifier'
|
||||
> & {
|
||||
flatApplicationMaps: FlatApplicationCacheMaps;
|
||||
allRelatedFlatEntityMaps: Partial<AllFlatEntityMaps>;
|
||||
}): string[] {
|
||||
const applicationIds = new Set<string>();
|
||||
|
||||
const applicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
applicationUniversalIdentifier
|
||||
];
|
||||
|
||||
const twentyStandardApplicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier
|
||||
];
|
||||
|
||||
if (!isDefined(twentyStandardApplicationId) || !isDefined(applicationId)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Application to build and its dependent application not found',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
applicationIds.add(applicationId);
|
||||
|
||||
const isBuildingTwentyStandardApplication =
|
||||
applicationUniversalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier;
|
||||
|
||||
if (!isBuildingTwentyStandardApplication) {
|
||||
applicationIds.add(twentyStandardApplicationId);
|
||||
}
|
||||
|
||||
for (const metadataName of Object.keys(
|
||||
allFlatEntityOperationByMetadataName,
|
||||
) as AllMetadataName[]) {
|
||||
const flatEntityOperations =
|
||||
allFlatEntityOperationByMetadataName[metadataName];
|
||||
|
||||
if (!isDefined(flatEntityOperations)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { flatEntityToCreate, flatEntityToUpdate, flatEntityToDelete } =
|
||||
flatEntityOperations;
|
||||
|
||||
const relations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName];
|
||||
|
||||
for (const flatEntity of [
|
||||
...flatEntityToCreate,
|
||||
...flatEntityToUpdate,
|
||||
...flatEntityToDelete,
|
||||
]) {
|
||||
const entityApplicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
flatEntity.applicationUniversalIdentifier
|
||||
];
|
||||
|
||||
if (isDefined(entityApplicationId)) {
|
||||
applicationIds.add(entityApplicationId);
|
||||
}
|
||||
|
||||
for (const relation of Object.values(relations) as ({
|
||||
foreignKey: string;
|
||||
metadataName: AllMetadataName;
|
||||
isNullable: boolean;
|
||||
universalForeignKey: string;
|
||||
} | null)[]) {
|
||||
if (!isDefined(relation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { universalForeignKey, metadataName: targetMetadataName } =
|
||||
relation;
|
||||
|
||||
const referencedUniversalIdentifier =
|
||||
flatEntity[universalForeignKey as keyof typeof flatEntity];
|
||||
|
||||
if (!isDefined(referencedUniversalIdentifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetFlatEntityMaps =
|
||||
allRelatedFlatEntityMaps[
|
||||
getMetadataFlatEntityMapsKey(
|
||||
targetMetadataName as AllMetadataName,
|
||||
)
|
||||
];
|
||||
|
||||
if (!isDefined(targetFlatEntityMaps)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const referencedEntity =
|
||||
targetFlatEntityMaps.byUniversalIdentifier[
|
||||
referencedUniversalIdentifier
|
||||
];
|
||||
|
||||
if (isDefined(referencedEntity)) {
|
||||
applicationIds.add(referencedEntity.applicationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...applicationIds];
|
||||
}
|
||||
|
||||
private async computeAllRelatedFlatEntityMaps({
|
||||
allFlatEntityOperationByMetadataName,
|
||||
workspaceId,
|
||||
@@ -105,26 +223,12 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
{},
|
||||
);
|
||||
|
||||
const twentyStandardApplicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier
|
||||
];
|
||||
|
||||
const applicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
applicationUniversalIdentifier
|
||||
];
|
||||
|
||||
if (!isDefined(twentyStandardApplicationId) || !isDefined(applicationId)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Application to build and its dependent application not found',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const isBuildingTwentyStandardApplication =
|
||||
applicationUniversalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier;
|
||||
const applicationIds = this.computeAllInvolvedApplicationIds({
|
||||
allFlatEntityOperationByMetadataName,
|
||||
flatApplicationMaps,
|
||||
applicationUniversalIdentifier,
|
||||
allRelatedFlatEntityMaps,
|
||||
});
|
||||
|
||||
const dependencyAllFlatEntityMaps = allMetadataNameCacheToCompute.reduce(
|
||||
(allFlatEntityMaps, metadataName) => {
|
||||
@@ -137,9 +241,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
getSubFlatEntityMapsByApplicationIdsOrThrow<
|
||||
MetadataFlatEntity<typeof metadataName>
|
||||
>({
|
||||
applicationIds: isBuildingTwentyStandardApplication
|
||||
? [applicationId]
|
||||
: [applicationId, twentyStandardApplicationId],
|
||||
applicationIds,
|
||||
flatEntityMaps:
|
||||
allRelatedFlatEntityMaps[metadataFlatEntityMapsKey],
|
||||
}),
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ exports[`CommandMenuItem creation should fail when creating with empty workflowV
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -72,7 +72,7 @@ exports[`CommandMenuItem creation should fail when creating with invalid workflo
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ exports[`CommandMenuItem deletion should fail when deleting with empty id 1`] =
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -51,7 +51,7 @@ exports[`CommandMenuItem deletion should fail when deleting with invalid id (not
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ exports[`CommandMenuItem update should fail when updating with empty id 1`] = `
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -72,7 +72,7 @@ exports[`CommandMenuItem update should fail when updating with invalid id (not a
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+6
-6
@@ -10,7 +10,7 @@ exports[`NavigationMenuItem creation should fail when creating with empty target
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -25,7 +25,7 @@ exports[`NavigationMenuItem creation should fail when creating with empty target
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -40,7 +40,7 @@ exports[`NavigationMenuItem creation should fail when creating with invalid fold
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -55,7 +55,7 @@ exports[`NavigationMenuItem creation should fail when creating with invalid targ
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -70,7 +70,7 @@ exports[`NavigationMenuItem creation should fail when creating with invalid targ
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -85,7 +85,7 @@ exports[`NavigationMenuItem creation should fail when creating with invalid user
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ exports[`NavigationMenuItem deletion should fail when deleting with empty id 1`]
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -37,7 +37,7 @@ exports[`NavigationMenuItem deletion should fail when deleting with invalid id (
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ exports[`NavigationMenuItem update should fail when updating with empty id 1`] =
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -37,7 +37,7 @@ exports[`NavigationMenuItem update should fail when updating with invalid folder
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -52,7 +52,7 @@ exports[`NavigationMenuItem update should fail when updating with invalid id (no
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-valid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -67,7 +67,7 @@ exports[`NavigationMenuItem update should fail when updating with missing id 1`]
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ exports[`Object metadata update should fail when labelIdentifier is not a uuid 1
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "not-a-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
},
|
||||
]
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ exports[`Row Level Permission Predicate upsert should fail when fieldMetadataId
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "invalid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -49,7 +49,7 @@ exports[`Row Level Permission Predicate upsert should fail when objectMetadataId
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "invalid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -64,7 +64,7 @@ exports[`Row Level Permission Predicate upsert should fail when objectMetadataId
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "invalid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
@@ -91,7 +91,7 @@ exports[`Row Level Permission Predicate upsert should fail when roleId is not a
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "invalid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ exports[`View Filter Group creation should fail when viewId is not a valid UUID
|
||||
"userFriendlyMessage": "An error occurred.",
|
||||
"value": "invalid-uuid",
|
||||
},
|
||||
"message": "Invalid UUID",
|
||||
"message": "Invalid UUID: 'not-a-valid-uuid'",
|
||||
"name": "ValidationError",
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user