Fix cross app installation (#18151)
# Introduction - Fixing app dependencyFlatEntityMaps load ( to load current app + dependent app ) - Fix empty flat entity maps and undefined optimistic override - Refactor the optimistic rendering to be mutation oriented at orchestrator level
This commit is contained in:
+11
-11
@@ -65,6 +65,11 @@ export class ApplicationManifestMigrationService {
|
||||
|
||||
const { featureFlagsMap, ...existingAllFlatEntityMaps } = cacheResult;
|
||||
|
||||
const fromAllFlatEntityMaps = getApplicationSubAllFlatEntityMaps({
|
||||
applicationIds: [ownerFlatApplication.id],
|
||||
fromAllFlatEntityMaps: existingAllFlatEntityMaps,
|
||||
});
|
||||
|
||||
const toAllUniversalFlatEntityMaps =
|
||||
computeApplicationManifestAllUniversalFlatEntityMaps({
|
||||
manifest,
|
||||
@@ -72,20 +77,15 @@ export class ApplicationManifestMigrationService {
|
||||
now,
|
||||
});
|
||||
|
||||
const fromAllFlatEntityMaps = getApplicationSubAllFlatEntityMaps({
|
||||
applicationIds: [ownerFlatApplication.id],
|
||||
const dependencyAllFlatEntityMaps = getApplicationSubAllFlatEntityMaps({
|
||||
applicationIds:
|
||||
ownerFlatApplication.universalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier
|
||||
? [twentyStandardFlatApplication.id]
|
||||
: [ownerFlatApplication.id, twentyStandardFlatApplication.id],
|
||||
fromAllFlatEntityMaps: existingAllFlatEntityMaps,
|
||||
});
|
||||
|
||||
const dependencyAllFlatEntityMaps =
|
||||
ownerFlatApplication.universalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier
|
||||
? undefined
|
||||
: getApplicationSubAllFlatEntityMaps({
|
||||
applicationIds: [twentyStandardFlatApplication.id],
|
||||
fromAllFlatEntityMaps: existingAllFlatEntityMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigrationFromTo(
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PackageJson } from 'type-fest';
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import {
|
||||
|
||||
+4
@@ -11,6 +11,10 @@ type MetadataRequiredForValidation = {
|
||||
};
|
||||
};
|
||||
|
||||
export type MetadataRelatedMetadataNameForValidation<
|
||||
T extends AllMetadataName,
|
||||
> = keyof (typeof ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION)[T];
|
||||
|
||||
// TODO deprecate in favor of ALL_METADATA_SERIALIZED_RELATION
|
||||
export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
|
||||
fieldMetadata: {
|
||||
|
||||
+3
@@ -12,6 +12,9 @@ type MetadataSerializedRelationProperties = {
|
||||
: Partial<Record<AllMetadataName, true>>;
|
||||
};
|
||||
|
||||
export type MetadataSerializedRelatedMetadataName<T extends AllMetadataName> =
|
||||
keyof (typeof ALL_METADATA_SERIALIZED_RELATION)[T];
|
||||
|
||||
export const ALL_METADATA_SERIALIZED_RELATION = {
|
||||
agent: {},
|
||||
skill: {},
|
||||
|
||||
+1
@@ -4,5 +4,6 @@ import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/works
|
||||
export type FlatEntityMaps<T extends SyncableFlatEntity> =
|
||||
UniversalFlatEntityMaps<T> & {
|
||||
universalIdentifierById: Partial<Record<string, string>>;
|
||||
// TODO refactor or replicate to be universal and UniversalFlatEntityMaps lvl located
|
||||
universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
|
||||
};
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import {
|
||||
ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION,
|
||||
type MetadataRelatedMetadataNameForValidation,
|
||||
} from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant';
|
||||
|
||||
export const getMetadataRelatedMetadataNamesForValidation = <
|
||||
T extends AllMetadataName,
|
||||
>(
|
||||
metadataName: T,
|
||||
) => {
|
||||
return Object.keys(
|
||||
ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION[metadataName],
|
||||
) as MetadataRelatedMetadataNameForValidation<T>[];
|
||||
};
|
||||
+8
-5
@@ -1,11 +1,14 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { ALL_METADATA_SERIALIZED_RELATION } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant';
|
||||
import {
|
||||
ALL_METADATA_SERIALIZED_RELATION,
|
||||
type MetadataSerializedRelatedMetadataName,
|
||||
} from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant';
|
||||
|
||||
export const getMetadataSerializedRelationNames = (
|
||||
metadataName: AllMetadataName,
|
||||
): AllMetadataName[] => {
|
||||
export const getMetadataSerializedRelationNames = <T extends AllMetadataName>(
|
||||
metadataName: T,
|
||||
) => {
|
||||
return Object.keys(
|
||||
ALL_METADATA_SERIALIZED_RELATION[metadataName],
|
||||
) as AllMetadataName[];
|
||||
) as MetadataSerializedRelatedMetadataName<T>[];
|
||||
};
|
||||
|
||||
+9
-5
@@ -1,8 +1,8 @@
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
export const getSubFlatEntityByIdsMapsOrThrow = <T extends SyncableFlatEntity>({
|
||||
flatEntityIds,
|
||||
@@ -11,15 +11,19 @@ export const getSubFlatEntityByIdsMapsOrThrow = <T extends SyncableFlatEntity>({
|
||||
flatEntityMaps: FlatEntityMaps<T>;
|
||||
flatEntityIds: string[];
|
||||
}): FlatEntityMaps<T> => {
|
||||
return flatEntityIds.reduce<FlatEntityMaps<T>>((acc, flatEntityId) => {
|
||||
const flatEntityMapsToMutate = createEmptyFlatEntityMaps();
|
||||
|
||||
flatEntityIds.forEach((flatEntityId) => {
|
||||
const flatEntity = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId,
|
||||
flatEntityMaps,
|
||||
});
|
||||
|
||||
return addFlatEntityToFlatEntityMapsOrThrow({
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity,
|
||||
flatEntityMaps: acc,
|
||||
flatEntityMapsToMutate,
|
||||
});
|
||||
}, createEmptyFlatEntityMaps());
|
||||
});
|
||||
|
||||
return flatEntityMapsToMutate;
|
||||
};
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-universal-identifier-in-universal-flat-entity-maps-or-throw.util';
|
||||
import { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-identifiers.util';
|
||||
import { validateObjectMetadataSystemFieldsIntegrity } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-object-metadata-system-fields-integrity.util';
|
||||
import {
|
||||
type OrchestratorActionsReport,
|
||||
type OrchestratorFailureReport,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { getEmptyFlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/utils/get-flat-entity-validation-error.util';
|
||||
|
||||
type ValidateObjectMetadataSystemFieldsIntegrityArgs = {
|
||||
orchestratorActionsReport: Pick<
|
||||
OrchestratorActionsReport,
|
||||
'fieldMetadata' | 'objectMetadata'
|
||||
>;
|
||||
optimisticUniversalFlatMaps: Pick<
|
||||
AllUniversalFlatEntityMaps,
|
||||
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps'
|
||||
>;
|
||||
};
|
||||
export const validateObjectMetadataCrossEntity = ({
|
||||
optimisticUniversalFlatMaps,
|
||||
orchestratorActionsReport,
|
||||
}: ValidateObjectMetadataSystemFieldsIntegrityArgs): Pick<
|
||||
OrchestratorFailureReport,
|
||||
'objectMetadata'
|
||||
> => {
|
||||
const metadataValidationErrors: Pick<
|
||||
OrchestratorFailureReport,
|
||||
'objectMetadata'
|
||||
> = {
|
||||
objectMetadata: [],
|
||||
};
|
||||
|
||||
const createdObjectMetadatas =
|
||||
findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow({
|
||||
universalIdentifiers: orchestratorActionsReport.objectMetadata.create.map(
|
||||
(createObjectAction) =>
|
||||
createObjectAction.flatEntity.universalIdentifier,
|
||||
),
|
||||
flatEntityMaps: optimisticUniversalFlatMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
for (const universalFlatObjectMetadata of createdObjectMetadatas) {
|
||||
const createFailedFlatEntityValidations = getEmptyFlatEntityValidationError(
|
||||
{
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier: universalFlatObjectMetadata.universalIdentifier,
|
||||
namePlural: universalFlatObjectMetadata.namePlural,
|
||||
nameSingular: universalFlatObjectMetadata.nameSingular,
|
||||
},
|
||||
metadataName: 'objectMetadata',
|
||||
type: 'create',
|
||||
},
|
||||
);
|
||||
|
||||
createFailedFlatEntityValidations.errors.push(
|
||||
...validateObjectMetadataSystemFieldsIntegrity({
|
||||
universalFlatFieldMetadataMaps:
|
||||
optimisticUniversalFlatMaps.flatFieldMetadataMaps,
|
||||
universalFlatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
createFailedFlatEntityValidations.errors.push(
|
||||
...validateFlatObjectMetadataIdentifiers({
|
||||
universalFlatObjectMetadata,
|
||||
universalFlatFieldMetadataMaps:
|
||||
optimisticUniversalFlatMaps.flatFieldMetadataMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
if (createFailedFlatEntityValidations.errors.length > 0) {
|
||||
metadataValidationErrors.objectMetadata.push(
|
||||
createFailedFlatEntityValidations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const objectMetadataUpdate of orchestratorActionsReport.objectMetadata
|
||||
.update) {
|
||||
const updatedFlatObjectMetadata =
|
||||
findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: optimisticUniversalFlatMaps.flatObjectMetadataMaps,
|
||||
universalIdentifier: objectMetadataUpdate.universalIdentifier,
|
||||
});
|
||||
|
||||
const updateFailedFlatEntityValidations = getEmptyFlatEntityValidationError(
|
||||
{
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier: updatedFlatObjectMetadata.universalIdentifier,
|
||||
namePlural: updatedFlatObjectMetadata.namePlural,
|
||||
nameSingular: updatedFlatObjectMetadata.nameSingular,
|
||||
},
|
||||
metadataName: 'objectMetadata',
|
||||
type: 'update',
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
objectMetadataUpdate.update
|
||||
.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
)
|
||||
) {
|
||||
updateFailedFlatEntityValidations.errors.push(
|
||||
...validateFlatObjectMetadataIdentifiers({
|
||||
universalFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
universalFlatFieldMetadataMaps:
|
||||
optimisticUniversalFlatMaps.flatFieldMetadataMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (updateFailedFlatEntityValidations.errors.length > 0) {
|
||||
metadataValidationErrors.objectMetadata.push(
|
||||
updateFailedFlatEntityValidations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return metadataValidationErrors;
|
||||
};
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-universal-identifier-in-universal-flat-entity-maps-or-throw.util';
|
||||
import { PARTIAL_SYSTEM_FLAT_FIELD_METADATAS } from 'src/engine/metadata-modules/object-metadata/constants/partial-system-flat-field-metadatas.constant';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import {
|
||||
type OrchestratorActionsReport,
|
||||
type OrchestratorFailureReport,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
import { getEmptyFlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/utils/get-flat-entity-validation-error.util';
|
||||
import { buildUniversalFlatObjectFieldByNameAndJoinColumnMaps } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/utils/build-universal-flat-object-field-by-name-and-join-column-maps.util';
|
||||
|
||||
type ValidateObjectMetadataSystemFieldsIntegrityArgs = {
|
||||
orchestratorActionsReport: Pick<
|
||||
OrchestratorActionsReport,
|
||||
'fieldMetadata' | 'objectMetadata'
|
||||
>;
|
||||
optimisticUniversalFlatMaps: Pick<
|
||||
AllUniversalFlatEntityMaps,
|
||||
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps'
|
||||
>;
|
||||
};
|
||||
export const validateObjectMetadataSystemFieldsIntegrity = ({
|
||||
optimisticUniversalFlatMaps,
|
||||
orchestratorActionsReport,
|
||||
}: ValidateObjectMetadataSystemFieldsIntegrityArgs): Pick<
|
||||
OrchestratorFailureReport,
|
||||
'objectMetadata'
|
||||
> => {
|
||||
const metadataValidationErrors: Pick<
|
||||
OrchestratorFailureReport,
|
||||
'objectMetadata'
|
||||
> = {
|
||||
objectMetadata: [],
|
||||
};
|
||||
|
||||
const createdObjectMetadatas =
|
||||
findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow({
|
||||
universalIdentifiers: orchestratorActionsReport.objectMetadata.create.map(
|
||||
(createObjectAction) =>
|
||||
createObjectAction.flatEntity.universalIdentifier,
|
||||
),
|
||||
flatEntityMaps: optimisticUniversalFlatMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
for (const createdObjectMetadata of createdObjectMetadatas) {
|
||||
const createdFailedFlatEntityValidations: FailedFlatEntityValidation<
|
||||
'objectMetadata',
|
||||
'create'
|
||||
> = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier: createdObjectMetadata.universalIdentifier,
|
||||
namePlural: createdObjectMetadata.namePlural,
|
||||
nameSingular: createdObjectMetadata.nameSingular,
|
||||
},
|
||||
metadataName: 'objectMetadata',
|
||||
type: 'create',
|
||||
});
|
||||
|
||||
const { fieldUniversalIdentifierByName } =
|
||||
buildUniversalFlatObjectFieldByNameAndJoinColumnMaps({
|
||||
flatFieldMetadataMaps:
|
||||
optimisticUniversalFlatMaps.flatFieldMetadataMaps,
|
||||
flatObjectMetadata: createdObjectMetadata,
|
||||
});
|
||||
|
||||
for (const expectedSystemField of Object.values(
|
||||
PARTIAL_SYSTEM_FLAT_FIELD_METADATAS,
|
||||
)) {
|
||||
const matchingFieldUniversalIdentifier =
|
||||
fieldUniversalIdentifierByName[expectedSystemField.name];
|
||||
|
||||
const expectedFieldName = expectedSystemField.name;
|
||||
|
||||
if (!isDefined(matchingFieldUniversalIdentifier)) {
|
||||
createdFailedFlatEntityValidations.errors.push({
|
||||
code: ObjectMetadataExceptionCode.MISSING_SYSTEM_FIELD,
|
||||
message: `System field ${expectedFieldName} is missing`,
|
||||
userFriendlyMessage: msg`System field ${expectedFieldName} is missing`,
|
||||
value: expectedFieldName,
|
||||
});
|
||||
} else {
|
||||
const universalFlatFieldMetadata =
|
||||
findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: optimisticUniversalFlatMaps.flatFieldMetadataMaps,
|
||||
universalIdentifier: matchingFieldUniversalIdentifier,
|
||||
});
|
||||
|
||||
const propertiesToValidate = [
|
||||
'type',
|
||||
'isSystem',
|
||||
] as const satisfies (keyof UniversalFlatFieldMetadata)[];
|
||||
|
||||
for (const property of propertiesToValidate) {
|
||||
const expectedValue = expectedSystemField[property];
|
||||
const actualValue = universalFlatFieldMetadata[property];
|
||||
|
||||
if (actualValue !== expectedValue) {
|
||||
createdFailedFlatEntityValidations.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_SYSTEM_FIELD,
|
||||
message: `System field ${expectedFieldName} has invalid ${property}: expected ${String(expectedValue)}, got ${String(actualValue)}`,
|
||||
userFriendlyMessage: msg`System field ${expectedFieldName} has invalid ${property}`,
|
||||
value: actualValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (createdFailedFlatEntityValidations.errors.length > 0) {
|
||||
metadataValidationErrors.objectMetadata.push(
|
||||
createdFailedFlatEntityValidations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return metadataValidationErrors;
|
||||
};
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { PARTIAL_SYSTEM_FLAT_FIELD_METADATAS } from 'src/engine/metadata-modules/object-metadata/constants/partial-system-flat-field-metadatas.constant';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
|
||||
import { type FlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
import { buildUniversalFlatObjectFieldByNameAndJoinColumnMaps } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/utils/build-universal-flat-object-field-by-name-and-join-column-maps.util';
|
||||
|
||||
type ValidateObjectMetadataSystemFieldsIntegrityArgs = {
|
||||
universalFlatObjectMetadata: UniversalFlatObjectMetadata;
|
||||
universalFlatFieldMetadataMaps: AllUniversalFlatEntityMaps['flatFieldMetadataMaps'];
|
||||
};
|
||||
export const validateObjectMetadataSystemFieldsIntegrity = ({
|
||||
universalFlatFieldMetadataMaps,
|
||||
universalFlatObjectMetadata,
|
||||
}: ValidateObjectMetadataSystemFieldsIntegrityArgs): FlatEntityValidationError[] => {
|
||||
const errors: FlatEntityValidationError[] = [];
|
||||
|
||||
const { fieldUniversalIdentifierByName } =
|
||||
buildUniversalFlatObjectFieldByNameAndJoinColumnMaps({
|
||||
flatFieldMetadataMaps: universalFlatFieldMetadataMaps,
|
||||
flatObjectMetadata: universalFlatObjectMetadata,
|
||||
});
|
||||
|
||||
for (const expectedSystemField of Object.values(
|
||||
PARTIAL_SYSTEM_FLAT_FIELD_METADATAS,
|
||||
)) {
|
||||
const matchingFieldUniversalIdentifier =
|
||||
fieldUniversalIdentifierByName[expectedSystemField.name];
|
||||
|
||||
const expectedFieldName = expectedSystemField.name;
|
||||
|
||||
if (!isDefined(matchingFieldUniversalIdentifier)) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.MISSING_SYSTEM_FIELD,
|
||||
message: `System field ${expectedFieldName} is missing`,
|
||||
userFriendlyMessage: msg`System field ${expectedFieldName} is missing`,
|
||||
value: expectedFieldName,
|
||||
});
|
||||
} else {
|
||||
const universalFlatFieldMetadata =
|
||||
findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: universalFlatFieldMetadataMaps,
|
||||
universalIdentifier: matchingFieldUniversalIdentifier,
|
||||
});
|
||||
|
||||
const propertiesToValidate = [
|
||||
'type',
|
||||
'isSystem',
|
||||
] as const satisfies (keyof UniversalFlatFieldMetadata)[];
|
||||
|
||||
for (const property of propertiesToValidate) {
|
||||
const expectedValue = expectedSystemField[property];
|
||||
const actualValue = universalFlatFieldMetadata[property];
|
||||
|
||||
if (actualValue !== expectedValue) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_SYSTEM_FIELD,
|
||||
message: `System field ${expectedFieldName} has invalid ${property}: expected ${String(expectedValue)}, got ${String(actualValue)}`,
|
||||
userFriendlyMessage: msg`System field ${expectedFieldName} has invalid ${property}`,
|
||||
value: actualValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
+1
@@ -20,6 +20,7 @@ import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspa
|
||||
import { FromToAllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
|
||||
// TODO completely deprecate this file once we've created the twenty-standard twenty-app manifest
|
||||
@Injectable()
|
||||
export class TwentyStandardApplicationService {
|
||||
constructor(
|
||||
|
||||
+31
-322
@@ -1,10 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { createEmptyAllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-all-flat-entity-maps.constant';
|
||||
import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
|
||||
import { createEmptyOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/constant/empty-orchestrator-actions-report.constant';
|
||||
import { EMPTY_ORCHESTRATOR_FAILURE_REPORT } from 'src/engine/workspace-manager/workspace-migration/constant/empty-orchestrator-failure-report.constant';
|
||||
import {
|
||||
@@ -74,6 +72,12 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
WorkspaceMigrationOrchestratorBuildArgs,
|
||||
'fromToAllFlatEntityMaps' | 'dependencyAllFlatEntityMaps'
|
||||
>): AllUniversalFlatEntityMaps {
|
||||
if (isDefined(dependencyAllFlatEntityMaps)) {
|
||||
return {
|
||||
...createEmptyAllFlatEntityMaps(),
|
||||
...dependencyAllFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
const allFromToFlatEntityMapsKeys = Object.keys(
|
||||
fromToAllFlatEntityMaps,
|
||||
) as (keyof AllUniversalFlatEntityMaps)[];
|
||||
@@ -82,10 +86,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
(allFlatEntityMaps, currFlatMaps) => {
|
||||
const fromToOccurence = fromToAllFlatEntityMaps[currFlatMaps];
|
||||
|
||||
if (
|
||||
!isDefined(fromToOccurence) ||
|
||||
isDefined(allFlatEntityMaps[currFlatMaps])
|
||||
) {
|
||||
if (!isDefined(fromToOccurence)) {
|
||||
return allFlatEntityMaps;
|
||||
}
|
||||
|
||||
@@ -96,31 +97,10 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
},
|
||||
{
|
||||
...createEmptyAllFlatEntityMaps(),
|
||||
...dependencyAllFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation<
|
||||
T extends AllMetadataName,
|
||||
>({
|
||||
allFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps,
|
||||
}: {
|
||||
flatEntityMapsAndRelatedFlatEntityMaps: MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<T>;
|
||||
allFlatEntityMaps: AllUniversalFlatEntityMaps;
|
||||
}) {
|
||||
const flatEntityMapsKeys = Object.keys(
|
||||
flatEntityMapsAndRelatedFlatEntityMaps,
|
||||
) as (keyof MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<T>)[];
|
||||
|
||||
for (const flatEntityMapsKey of flatEntityMapsKeys) {
|
||||
// @ts-expect-error TODO improve
|
||||
allFlatEntityMaps[flatEntityMapsKey] =
|
||||
flatEntityMapsAndRelatedFlatEntityMaps[flatEntityMapsKey];
|
||||
}
|
||||
}
|
||||
|
||||
public async buildWorkspaceMigration({
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
@@ -142,6 +122,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
fromToAllFlatEntityMaps,
|
||||
dependencyAllFlatEntityMaps,
|
||||
});
|
||||
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewFieldMaps,
|
||||
@@ -177,32 +158,13 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
{
|
||||
additionalCacheDataMaps,
|
||||
buildOptions,
|
||||
// Note: That's a hacky way to allow validating object against field metadatas, not optimal
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps: {
|
||||
byUniversalIdentifier: {
|
||||
...dependencyAllFlatEntityMaps?.flatFieldMetadataMaps
|
||||
?.byUniversalIdentifier,
|
||||
...flatFieldMetadataMaps?.from.byUniversalIdentifier,
|
||||
...flatFieldMetadataMaps?.to.byUniversalIdentifier,
|
||||
},
|
||||
},
|
||||
},
|
||||
///
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
from: fromFlatObjectMetadataMaps,
|
||||
to: toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
objectResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (objectResult.status === 'fail') {
|
||||
orchestratorFailureReport.objectMetadata.push(...objectResult.errors);
|
||||
} else {
|
||||
@@ -220,22 +182,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatFieldMetadataMaps,
|
||||
to: toFlatFieldMetadataMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
fieldResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (fieldResult.status === 'fail') {
|
||||
orchestratorFailureReport.fieldMetadata.push(...fieldResult.errors);
|
||||
} else {
|
||||
@@ -252,24 +203,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatIndexMaps,
|
||||
to: toFlatIndexMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
indexResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (indexResult.status === 'fail') {
|
||||
orchestratorFailureReport.index.push(...indexResult.errors);
|
||||
} else {
|
||||
@@ -283,12 +221,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
await this.workspaceMigrationViewActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
additionalCacheDataMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
from: fromFlatViewMaps,
|
||||
to: toFlatViewMaps,
|
||||
buildOptions,
|
||||
@@ -296,14 +229,6 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewResult.status === 'fail') {
|
||||
orchestratorFailureReport.view.push(...viewResult.errors);
|
||||
} else {
|
||||
@@ -322,21 +247,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewFieldGroupMaps,
|
||||
to: toFlatViewFieldGroupMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewFieldGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewFieldGroupResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewFieldGroup.push(
|
||||
...viewFieldGroupResult.errors,
|
||||
@@ -356,27 +271,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewFieldMaps,
|
||||
to: toFlatViewFieldMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
flatViewFieldGroupMaps:
|
||||
optimisticAllFlatEntityMaps.flatViewFieldGroupMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewFieldResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewFieldResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewField.push(...viewFieldResult.errors);
|
||||
} else {
|
||||
@@ -397,21 +296,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewFilterGroupMaps,
|
||||
to: toFlatViewFilterGroupMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewFilterGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewFilterGroupResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewFilterGroup.push(
|
||||
...viewFilterGroupResult.errors,
|
||||
@@ -432,25 +321,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewFilterMaps,
|
||||
to: toFlatViewFilterMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
flatViewFilterGroupMaps:
|
||||
optimisticAllFlatEntityMaps.flatViewFilterGroupMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewFilterResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewFilterResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewFilter.push(...viewFilterResult.errors);
|
||||
} else {
|
||||
@@ -468,23 +343,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewGroupMaps,
|
||||
to: toFlatViewGroupMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
viewGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (viewGroupResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewGroup.push(...viewGroupResult.errors);
|
||||
} else {
|
||||
@@ -504,23 +367,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatRowLevelPermissionPredicateGroupMaps,
|
||||
to: toFlatRowLevelPermissionPredicateGroupMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps,
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
rowLevelPermissionPredicateGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (rowLevelPermissionPredicateGroupResult.status === 'fail') {
|
||||
orchestratorFailureReport.rowLevelPermissionPredicateGroup.push(
|
||||
...rowLevelPermissionPredicateGroupResult.errors,
|
||||
@@ -543,27 +394,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatRowLevelPermissionPredicateMaps,
|
||||
to: toFlatRowLevelPermissionPredicateMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatRowLevelPermissionPredicateGroupMaps:
|
||||
optimisticAllFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps,
|
||||
flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
rowLevelPermissionPredicateResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (rowLevelPermissionPredicateResult.status === 'fail') {
|
||||
orchestratorFailureReport.rowLevelPermissionPredicate.push(
|
||||
...rowLevelPermissionPredicateResult.errors,
|
||||
@@ -585,19 +420,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatLogicFunctionMaps,
|
||||
to: toFlatLogicFunctionMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
logicFunctionResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (logicFunctionResult.status === 'fail') {
|
||||
orchestratorFailureReport.logicFunction.push(
|
||||
...logicFunctionResult.errors,
|
||||
@@ -617,19 +444,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatRoleMaps,
|
||||
to: toFlatRoleMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
roleResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (roleResult.status === 'fail') {
|
||||
orchestratorFailureReport.role.push(...roleResult.errors);
|
||||
} else {
|
||||
@@ -648,22 +467,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatRoleTargetMaps,
|
||||
to: toFlatRoleTargetMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatAgentMaps: optimisticAllFlatEntityMaps.flatAgentMaps,
|
||||
flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
roleTargetResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (roleTargetResult.status === 'fail') {
|
||||
orchestratorFailureReport.roleTarget.push(...roleTargetResult.errors);
|
||||
} else {
|
||||
@@ -681,21 +489,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatAgentMaps,
|
||||
to: toFlatAgentMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
agentResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (agentResult.status === 'fail') {
|
||||
orchestratorFailureReport.agent.push(...agentResult.errors);
|
||||
} else {
|
||||
@@ -713,19 +511,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatSkillMaps,
|
||||
to: toFlatSkillMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
skillResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (skillResult.status === 'fail') {
|
||||
orchestratorFailureReport.skill.push(...skillResult.errors);
|
||||
} else {
|
||||
@@ -746,24 +536,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatCommandMenuItemMaps,
|
||||
to: toFlatCommandMenuItemMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatFrontComponentMaps:
|
||||
optimisticAllFlatEntityMaps.flatFrontComponentMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
commandMenuItemResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (commandMenuItemResult.status === 'fail') {
|
||||
orchestratorFailureReport.commandMenuItem.push(
|
||||
...commandMenuItemResult.errors,
|
||||
@@ -787,23 +564,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatNavigationMenuItemMaps,
|
||||
to: toFlatNavigationMenuItemMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
navigationMenuItemResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (navigationMenuItemResult.status === 'fail') {
|
||||
orchestratorFailureReport.navigationMenuItem.push(
|
||||
...navigationMenuItemResult.errors,
|
||||
@@ -825,24 +590,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatPageLayoutMaps,
|
||||
to: toFlatPageLayoutMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatPageLayoutTabMaps:
|
||||
optimisticAllFlatEntityMaps.flatPageLayoutTabMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
pageLayoutResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (pageLayoutResult.status === 'fail') {
|
||||
orchestratorFailureReport.pageLayout.push(...pageLayoutResult.errors);
|
||||
} else {
|
||||
@@ -850,9 +602,6 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
}
|
||||
}
|
||||
|
||||
// Page layout tabs must be processed before page layout widgets because
|
||||
// widgets reference tabs, and the optimistic cache needs to contain the
|
||||
// newly created tabs before widget validation runs
|
||||
if (isDefined(flatPageLayoutTabMaps)) {
|
||||
const { from: fromFlatPageLayoutTabMaps, to: toFlatPageLayoutTabMaps } =
|
||||
flatPageLayoutTabMaps;
|
||||
@@ -864,22 +613,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatPageLayoutTabMaps,
|
||||
to: toFlatPageLayoutTabMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatPageLayoutMaps:
|
||||
optimisticAllFlatEntityMaps.flatPageLayoutMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
pageLayoutTabResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (pageLayoutTabResult.status === 'fail') {
|
||||
orchestratorFailureReport.pageLayoutTab.push(
|
||||
...pageLayoutTabResult.errors,
|
||||
@@ -902,24 +640,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatPageLayoutWidgetMaps,
|
||||
to: toFlatPageLayoutWidgetMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatPageLayoutTabMaps:
|
||||
optimisticAllFlatEntityMaps.flatPageLayoutTabMaps,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
pageLayoutWidgetResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (pageLayoutWidgetResult.status === 'fail') {
|
||||
orchestratorFailureReport.pageLayoutWidget.push(
|
||||
...pageLayoutWidgetResult.errors,
|
||||
@@ -941,19 +666,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatFrontComponentMaps,
|
||||
to: toFlatFrontComponentMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
frontComponentResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (frontComponentResult.status === 'fail') {
|
||||
orchestratorFailureReport.frontComponent.push(
|
||||
...frontComponentResult.errors,
|
||||
@@ -974,19 +691,11 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatWebhookMaps,
|
||||
to: toFlatWebhookMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
|
||||
{
|
||||
allFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
flatEntityMapsAndRelatedFlatEntityMaps:
|
||||
webhookResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (webhookResult.status === 'fail') {
|
||||
orchestratorFailureReport.webhook.push(...webhookResult.errors);
|
||||
} else {
|
||||
|
||||
+59
-20
@@ -8,13 +8,20 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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_METADATA_REQUIRED_METADATA_FOR_VALIDATION } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityToCreateDeleteUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-to-create-delete-update.type';
|
||||
import { MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
|
||||
import { MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
import { getMetadataRelatedMetadataNamesForValidation } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names-for-validation.util';
|
||||
import { getSubFlatEntityMapsByApplicationIdsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-maps-by-application-ids-or-throw.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { WorkspaceMigrationV2Exception } from 'src/engine/workspace-manager/workspace-migration.exception';
|
||||
import { WORKSPACE_MIGRATION_ADDITIONAL_CACHE_DATA_MAPS_KEY } from 'src/engine/workspace-manager/workspace-migration/constant/workspace-migration-additional-cache-data-maps-key.constant';
|
||||
import {
|
||||
@@ -29,7 +36,7 @@ import {
|
||||
WorkspaceMigrationOrchestratorFailedResult,
|
||||
WorkspaceMigrationOrchestratorSuccessfulResult,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { computeUniversalFlatEntityMapsFromTo } from 'src/engine/workspace-manager/workspace-migration/utils/compute-universal-flat-entity-maps-from-to.util';
|
||||
import { computeUniversalFlatEntityMapsFromToThroughMutation } from 'src/engine/workspace-manager/workspace-migration/utils/compute-universal-flat-entity-maps-from-to-through-mutation.util';
|
||||
import { InferDeletionFromMissingEntities } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/infer-deletion-from-missing-entities.type';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service';
|
||||
|
||||
@@ -39,8 +46,6 @@ type ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs = {
|
||||
[P in AllMetadataName]?: FlatEntityToCreateDeleteUpdate<P>;
|
||||
};
|
||||
isSystemBuild?: boolean;
|
||||
// TODO remove once application synchronization do not consume services atomically anymore
|
||||
// Should always be the universal workspace custom app id
|
||||
applicationUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
@@ -66,29 +71,30 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
private async computeAllRelatedFlatEntityMaps({
|
||||
allFlatEntityOperationByMetadataName,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
}: ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs) {
|
||||
const allMetadataNameToCompare = Object.keys(
|
||||
allFlatEntityOperationByMetadataName,
|
||||
) as AllMetadataName[];
|
||||
const allDependencyMetadataName = allMetadataNameToCompare.flatMap(
|
||||
(metadataName) =>
|
||||
Object.keys(
|
||||
ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION[metadataName],
|
||||
) as AllMetadataName[],
|
||||
);
|
||||
const allMetadataNameCacheToCompute = [
|
||||
...new Set([...allMetadataNameToCompare, ...allDependencyMetadataName]),
|
||||
...new Set([
|
||||
...allMetadataNameToCompare,
|
||||
...allMetadataNameToCompare.flatMap(
|
||||
getMetadataRelatedMetadataNamesForValidation,
|
||||
),
|
||||
]),
|
||||
];
|
||||
const allFlatEntityMapsCacheKeysToCompute =
|
||||
allMetadataNameCacheToCompute.map(getMetadataFlatEntityMapsKey);
|
||||
|
||||
const allRelatedFlatEntityMaps =
|
||||
const { flatApplicationMaps, ...allRelatedFlatEntityMaps } =
|
||||
await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
...allFlatEntityMapsCacheKeysToCompute,
|
||||
...WORKSPACE_MIGRATION_ADDITIONAL_CACHE_DATA_MAPS_KEY,
|
||||
'flatApplicationMaps',
|
||||
]);
|
||||
|
||||
const initialAccumulator = allDependencyMetadataName.reduce<
|
||||
const initialAccumulator = allMetadataNameCacheToCompute.reduce<
|
||||
Partial<AllFlatEntityMaps>
|
||||
>(
|
||||
(allFlatEntityMaps, metadataName) => ({
|
||||
@@ -98,7 +104,29 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const dependencyAllFlatEntityMaps = allDependencyMetadataName.reduce(
|
||||
|
||||
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 dependencyAllFlatEntityMaps = allMetadataNameCacheToCompute.reduce(
|
||||
(allFlatEntityMaps, metadataName) => {
|
||||
const metadataFlatEntityMapsKey =
|
||||
getMetadataFlatEntityMapsKey(metadataName);
|
||||
@@ -106,7 +134,15 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
return {
|
||||
...allFlatEntityMaps,
|
||||
[metadataFlatEntityMapsKey]:
|
||||
allRelatedFlatEntityMaps[metadataFlatEntityMapsKey],
|
||||
getSubFlatEntityMapsByApplicationIdsOrThrow<
|
||||
MetadataFlatEntity<typeof metadataName>
|
||||
>({
|
||||
applicationIds: isBuildingTwentyStandardApplication
|
||||
? [applicationId]
|
||||
: [applicationId, twentyStandardApplicationId],
|
||||
flatEntityMaps:
|
||||
allRelatedFlatEntityMaps[metadataFlatEntityMapsKey],
|
||||
}),
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
@@ -153,10 +189,10 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
});
|
||||
|
||||
const fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps = {};
|
||||
const inferDeletionFromMissingEntities: InferDeletionFromMissingEntities =
|
||||
{};
|
||||
const idByUniversalIdentifierByMetadataName: IdByUniversalIdentifierByMetadataName =
|
||||
{};
|
||||
const inferDeletionFromMissingEntities: InferDeletionFromMissingEntities =
|
||||
{};
|
||||
const allMetadataNameToCompare = Object.keys(
|
||||
allFlatEntityOperationByMetadataName,
|
||||
) as AllMetadataName[];
|
||||
@@ -166,7 +202,10 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
allFlatEntityOperationByMetadataName[metadataName];
|
||||
|
||||
if (!isDefined(flatEntityOperations)) {
|
||||
throw new Error('Should never occurs');
|
||||
throw new FlatEntityMapsException(
|
||||
`Could not load flat entity maps to compare for ${metadataName}, should never occur`,
|
||||
FlatEntityMapsExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const { flatEntityToCreate, flatEntityToDelete, flatEntityToUpdate } =
|
||||
flatEntityOperations;
|
||||
@@ -193,8 +232,8 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
|
||||
// @ts-expect-error Metadata flat entity maps cache key and metadataName colliding
|
||||
fromToAllFlatEntityMaps[flatEntityMapsKey] =
|
||||
computeUniversalFlatEntityMapsFromTo({
|
||||
flatEntityMaps,
|
||||
computeUniversalFlatEntityMapsFromToThroughMutation({
|
||||
flatEntityMaps: structuredClone(flatEntityMaps),
|
||||
flatEntityToCreate,
|
||||
flatEntityToDelete,
|
||||
flatEntityToUpdate,
|
||||
|
||||
+4
-1
@@ -21,8 +21,11 @@ export type WorkspaceMigrationOrchestratorBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps;
|
||||
dependencyAllFlatEntityMaps?: Partial<AllUniversalFlatEntityMaps>;
|
||||
additionalCacheDataMaps: WorkspaceMigrationBuilderAdditionalCacheDataMaps;
|
||||
/**
|
||||
* Dependency maps must contain current application and its dependent app app flat entity maps
|
||||
*/
|
||||
dependencyAllFlatEntityMaps?: Partial<AllUniversalFlatEntityMaps>;
|
||||
};
|
||||
|
||||
export type OrchestratorFailureReport = {
|
||||
|
||||
+8
-10
@@ -13,7 +13,8 @@ export type ComputeUniversalFlatEntityMapsFromToArgs<
|
||||
flatEntityMaps: MetadataUniversalFlatEntityMaps<T>;
|
||||
} & FlatEntityToCreateDeleteUpdate<T>;
|
||||
|
||||
export const computeUniversalFlatEntityMapsFromTo = <
|
||||
// Note: We don't load the whole application flat entity maps in the from for performances purposes
|
||||
export const computeUniversalFlatEntityMapsFromToThroughMutation = <
|
||||
T extends AllMetadataName,
|
||||
>({
|
||||
flatEntityMaps,
|
||||
@@ -25,15 +26,12 @@ export const computeUniversalFlatEntityMapsFromTo = <
|
||||
to: MetadataUniversalFlatEntityMaps<T>;
|
||||
} => {
|
||||
const fromFlatEntityMaps =
|
||||
flatEntityToDelete.length > 0
|
||||
? getSubUniversalFlatEntityByUniversalIdentifiersMapsOrThrow({
|
||||
universalIdentifiers: [
|
||||
...flatEntityToDelete,
|
||||
...flatEntityToUpdate,
|
||||
].map(({ universalIdentifier }) => universalIdentifier),
|
||||
universalFlatEntityMaps: flatEntityMaps,
|
||||
})
|
||||
: flatEntityMaps;
|
||||
getSubUniversalFlatEntityByUniversalIdentifiersMapsOrThrow({
|
||||
universalIdentifiers: [...flatEntityToDelete, ...flatEntityToUpdate].map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
universalFlatEntityMaps: flatEntityMaps,
|
||||
});
|
||||
|
||||
const toFlatEntityMaps: MetadataUniversalFlatEntityMaps<T> =
|
||||
structuredClone(fromFlatEntityMaps);
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { validateObjectMetadataSystemFieldsIntegrity } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-object-metadata-system-fields-integrity';
|
||||
import { validateObjectMetadataCrossEntity } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-object-metadata-cross-entity.util';
|
||||
import {
|
||||
type OrchestratorActionsReport,
|
||||
type OrchestratorFailureReport,
|
||||
@@ -12,7 +12,7 @@ export const crossEntityTransversalValidation = ({
|
||||
optimisticUniversalFlatMaps: AllUniversalFlatEntityMaps;
|
||||
orchestratorActionsReport: OrchestratorActionsReport;
|
||||
}): Pick<OrchestratorFailureReport, 'objectMetadata'> => {
|
||||
const { objectMetadata } = validateObjectMetadataSystemFieldsIntegrity({
|
||||
const { objectMetadata } = validateObjectMetadataCrossEntity({
|
||||
optimisticUniversalFlatMaps,
|
||||
orchestratorActionsReport,
|
||||
});
|
||||
|
||||
+4
-11
@@ -9,14 +9,13 @@ import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { MetadataUniversalFlatEntityAndRelatedFlatEntityMapsForValidation } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-and-related-flat-entity-maps-for-validation.type';
|
||||
import { MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
|
||||
import { MetadataValidationRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
|
||||
import { MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type';
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
import { WorkspaceMigrationBuilderAdditionalCacheDataMaps } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-builder-additional-cache-data-maps.type';
|
||||
import { AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { MetadataUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-maps.type';
|
||||
import { addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
|
||||
import { deleteUniversalFlatEntityForeignKeyAggregators } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-foreign-key-aggregators.util';
|
||||
@@ -37,7 +36,7 @@ import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-mana
|
||||
|
||||
export type ValidateAndBuildArgs<T extends AllMetadataName> = {
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
dependencyOptimisticFlatEntityMaps: MetadataValidationRelatedUniversalFlatEntityMaps<T>;
|
||||
dependencyOptimisticFlatEntityMaps: AllUniversalFlatEntityMaps;
|
||||
workspaceId: string;
|
||||
additionalCacheDataMaps: WorkspaceMigrationBuilderAdditionalCacheDataMaps;
|
||||
} & FromTo<MetadataUniversalFlatEntityMaps<T>>;
|
||||
@@ -59,7 +58,8 @@ export abstract class WorkspaceEntityMigrationBuilderService<
|
||||
|
||||
public async validateAndBuild({
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: inputDependencyOptimisticFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps:
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
from: fromFlatEntityMaps,
|
||||
to: toFlatEntityMaps,
|
||||
additionalCacheDataMaps,
|
||||
@@ -96,11 +96,6 @@ export abstract class WorkspaceEntityMigrationBuilderService<
|
||||
this.logger.time(`EntityBuilder ${this.metadataName}`, 'entity processing');
|
||||
|
||||
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(this.metadataName);
|
||||
const optimisticFlatEntityMapsAndRelatedFlatEntityMaps = {
|
||||
[flatEntityMapsKey]: structuredClone(fromFlatEntityMaps),
|
||||
...structuredClone(inputDependencyOptimisticFlatEntityMaps),
|
||||
} as MetadataUniversalFlatEntityAndRelatedFlatEntityMapsForValidation<T>;
|
||||
|
||||
const actionsResult = getMetadataEmptyWorkspaceMigrationActionRecord(
|
||||
this.metadataName,
|
||||
);
|
||||
@@ -322,7 +317,6 @@ export abstract class WorkspaceEntityMigrationBuilderService<
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: allValidationResult,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -334,7 +328,6 @@ export abstract class WorkspaceEntityMigrationBuilderService<
|
||||
return {
|
||||
status: 'success',
|
||||
actions: actionsResult,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -1,11 +1,9 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
|
||||
import { type WorkspaceMigrationActionType } from 'src/engine/metadata-modules/flat-entity/types/metadata-workspace-migration-action.type';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
export type FailedFlatEntityValidateAndBuild<T extends AllMetadataName> = {
|
||||
status: 'fail';
|
||||
errors: FailedFlatEntityValidation<T, WorkspaceMigrationActionType>[];
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<T>;
|
||||
};
|
||||
|
||||
-2
@@ -1,10 +1,8 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
|
||||
import { type MetadataUniversalWorkspaceMigrationActionsRecord } from 'src/engine/metadata-modules/flat-entity/types/metadata-workspace-migration-action.type';
|
||||
|
||||
export type SuccessfulFlatEntityValidateAndBuild<T extends AllMetadataName> = {
|
||||
status: 'success';
|
||||
actions: MetadataUniversalWorkspaceMigrationActionsRecord<T>;
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<T>;
|
||||
};
|
||||
|
||||
-16
@@ -5,7 +5,6 @@ import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-identifiers.util';
|
||||
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 { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
@@ -23,7 +22,6 @@ export class FlatObjectMetadataValidatorService {
|
||||
buildOptions,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
typeof ALL_METADATA_NAME.objectMetadata
|
||||
@@ -93,13 +91,6 @@ export class FlatObjectMetadataValidatorService {
|
||||
userFriendlyMessage: msg`Field label identifier is required`,
|
||||
});
|
||||
}
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateFlatObjectMetadataIdentifiers({
|
||||
universalFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
universalFlatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
@@ -175,7 +166,6 @@ export class FlatObjectMetadataValidatorService {
|
||||
flatEntityToValidate: flatObjectMetadataToValidate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: optimisticUniversalFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
@@ -214,12 +204,6 @@ export class FlatObjectMetadataValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
objectValidationResult.errors.push(
|
||||
...validateFlatObjectMetadataIdentifiers({
|
||||
universalFlatObjectMetadata: flatObjectMetadataToValidate,
|
||||
universalFlatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
}),
|
||||
);
|
||||
objectValidationResult.errors.push(
|
||||
...validateFlatObjectMetadataNameAndLabels({
|
||||
optimisticUniversalFlatObjectMetadataMaps,
|
||||
|
||||
+63
@@ -1,5 +1,68 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`syncApplication should create a TEXT field on the standard Company object 1`] = `
|
||||
{
|
||||
"syncApplication": {
|
||||
"actions": [
|
||||
{
|
||||
"flatEntity": {
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"createdAt": Any<String>,
|
||||
"defaultValue": null,
|
||||
"description": "The industry of the company",
|
||||
"icon": "IconBuildingFactory2",
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isLabelSyncedWithName": false,
|
||||
"isNullable": true,
|
||||
"isSystem": false,
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"label": "Industry",
|
||||
"morphId": null,
|
||||
"name": "industry",
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": null,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": null,
|
||||
"standardOverrides": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": null,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
"metadataName": "fieldMetadata",
|
||||
"type": "create",
|
||||
},
|
||||
{
|
||||
"flatEntity": {
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"canAccessAllTools": false,
|
||||
"canBeAssignedToAgents": true,
|
||||
"canBeAssignedToApiKeys": true,
|
||||
"canBeAssignedToUsers": true,
|
||||
"canDestroyAllObjectRecords": false,
|
||||
"canReadAllObjectRecords": false,
|
||||
"canSoftDeleteAllObjectRecords": false,
|
||||
"canUpdateAllObjectRecords": false,
|
||||
"canUpdateAllSettings": false,
|
||||
"createdAt": Any<String>,
|
||||
"description": "A test role",
|
||||
"icon": null,
|
||||
"isEditable": true,
|
||||
"label": "Test Role",
|
||||
"universalIdentifier": Any<String>,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
"metadataName": "role",
|
||||
"type": "create",
|
||||
},
|
||||
],
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`syncApplication should return workspace migration actions on initial sync then on second sync with field rename and new role 1`] = `
|
||||
{
|
||||
"syncApplication": {
|
||||
|
||||
+55
@@ -4,6 +4,7 @@ import { syncApplication } from 'test/integration/metadata/suites/application/ut
|
||||
import { uninstallApplication } from 'test/integration/metadata/suites/application/utils/uninstall-application.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -207,4 +208,58 @@ describe('syncApplication', () => {
|
||||
extractRecordIdsAndDatesAsExpectAny(secondSyncData),
|
||||
);
|
||||
}, 60000);
|
||||
|
||||
it('should create a TEXT field on the standard Company object', async () => {
|
||||
const companyFieldId = uuidv4();
|
||||
|
||||
const manifest: Manifest = {
|
||||
application: {
|
||||
universalIdentifier: TEST_APP_ID,
|
||||
defaultRoleUniversalIdentifier: TEST_ROLE_ID,
|
||||
displayName: 'Test Application',
|
||||
description: 'A test application for workspace migration',
|
||||
icon: 'IconTestPipe',
|
||||
applicationVariables: {},
|
||||
packageJsonChecksum: null,
|
||||
yarnLockChecksum: null,
|
||||
apiClientChecksum: null,
|
||||
},
|
||||
roles: [
|
||||
{
|
||||
universalIdentifier: TEST_ROLE_ID,
|
||||
label: 'Test Role',
|
||||
description: 'A test role',
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
objects: [],
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: companyFieldId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'industry',
|
||||
label: 'Industry',
|
||||
description: 'The industry of the company',
|
||||
icon: 'IconBuildingFactory2',
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECTS.company.universalIdentifier,
|
||||
},
|
||||
],
|
||||
logicFunctions: [],
|
||||
frontComponents: [],
|
||||
publicAssets: [],
|
||||
views: [],
|
||||
navigationMenuItems: [],
|
||||
pageLayouts: [],
|
||||
};
|
||||
|
||||
const { data: syncData } = await syncApplication({
|
||||
manifest,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(syncData).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(syncData),
|
||||
);
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
+2
-40
@@ -21,52 +21,14 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o
|
||||
"universalIdentifier": Any<String>,
|
||||
},
|
||||
"metadataName": "objectMetadata",
|
||||
"status": "fail",
|
||||
"type": "update",
|
||||
},
|
||||
],
|
||||
"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": {
|
||||
"fieldMetadataUniversalIdentifier": Any<String>,
|
||||
"universalIdentifier": Any<String>,
|
||||
"viewUniversalIdentifier": Any<String>,
|
||||
},
|
||||
"metadataName": "viewField",
|
||||
"status": "fail",
|
||||
"type": "create",
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
"fieldMetadataUniversalIdentifier": Any<String>,
|
||||
"universalIdentifier": Any<String>,
|
||||
"viewUniversalIdentifier": Any<String>,
|
||||
},
|
||||
"metadataName": "viewField",
|
||||
"status": "fail",
|
||||
"type": "create",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 1 objectMetadata, 2 viewFields",
|
||||
"message": "Validation failed for 1 objectMetadata",
|
||||
"summary": {
|
||||
"objectMetadata": 1,
|
||||
"totalErrors": 3,
|
||||
"viewField": 2,
|
||||
"totalErrors": 1,
|
||||
},
|
||||
"userFriendlyMessage": "Metadata validation failed",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user