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:
Paul Rastoin
2026-02-23 19:58:06 +01:00
committed by GitHub
parent 0d4fe4575b
commit af0af0a237
24 changed files with 481 additions and 571 deletions
@@ -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,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 {
@@ -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: {
@@ -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: {},
@@ -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[]>>;
};
@@ -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>[];
};
@@ -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>[];
};
@@ -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;
};
@@ -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;
};
@@ -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;
};
@@ -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;
};
@@ -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(
@@ -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 {
@@ -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,
@@ -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 = {
@@ -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);
@@ -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,
});
@@ -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,
};
}
@@ -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>;
};
@@ -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>;
};
@@ -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,
@@ -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": {
@@ -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);
});
@@ -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",
},