Refactor and standardize isSystem field and object (#17992)
# Introduction ## Centralize system field definitions - Extract a single `PARTIAL_SYSTEM_FLAT_FIELD_METADATAS` constant as the source of truth for all 8 system fields (`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`), eliminating duplication across custom object and standard app field builders - Refactor `buildDefaultFlatFieldMetadatasForCustomObject` to use the shared constant via a new `buildObjectSystemFlatFieldMetadatas` helper ## Mark system fields as `isSystem: true` - Fields `id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector` are now properly flagged as system fields across all standard objects and custom object creation - Standard app field builders for all ~30 standard objects updated to set `isSystem: true` on `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy` - System-only standard objects (blocklist, calendar channels, message threads, etc.) now also include `createdBy`, `updatedBy`, `position`, `searchVector` field definitions that were previously missing ## Validate system fields on object creation - New transversal validation (`crossEntityTransversalValidation`) runs after all atomic entity validations in the build orchestrator, ensuring all 8 system fields are present with correct `type` and `isSystem: true` when an object is created - New `buildUniversalFlatObjectFieldByNameAndJoinColumnMaps` utility to resolve field names to universal identifiers for a given object - New exception codes: `MISSING_SYSTEM_FIELD` and `INVALID_SYSTEM_FIELD` on `ObjectMetadataExceptionCode` ## Protect system fields and objects from mutation - Field validators now block update/delete of `isSystem` fields by non-system callers (`FIELD_MUTATION_NOT_ALLOWED`) - Object validators now block update/delete of `isSystem` objects by non-system callers - `POSITION` and `TS_VECTOR` field type validators replaced: instead of rejecting creation outright, they now validate that the field is named correctly (`position` / `searchVector`) and has `isSystem: true` ## Distinguish `isSystemBuild` from `isCallerTwentyStandardApp` - New `isCallerTwentyStandardApp` utility checks whether the caller's `applicationUniversalIdentifier` matches the twenty standard app - Name-sync logic (`isFlatFieldMetadataNameSyncedWithLabel`, `areFlatObjectMetadataNamesSyncedWithLabels`) refactored to use `isCallerTwentyStandardApp` for custom suffix decisions, keeping `isSystemBuild` for mutation permission checks - `WorkspaceMigrationBuilderOptions` type updated to include `applicationUniversalIdentifier` ## Adapt frontend filtering - New `HIDDEN_SYSTEM_FIELD_NAMES` constant (`id`, `position`, `searchVector`) and `isHiddenSystemField` utility to only hide truly internal fields while keeping user-facing system fields (`createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`) visible in the UI - ~20 frontend files updated to replace `!field.isSystem` checks with `!isHiddenSystemField(field)` across record index, settings, data model, charts, workflows, spreadsheet import, aggregations, and role permissions ## Add 1.19 upgrade commands - **`backfill-system-fields-is-system`**: Raw SQL command to set `isSystem = true` on existing workspace fields matching system field names, and fix `position` field type from `NUMBER` to `POSITION` for `favorite`/`favoriteFolder` objects. Includes proper cache invalidation. - **`add-missing-system-fields-to-standard-objects`**: Codegen'd workspace migration to create missing `position`, `searchVector`, `createdBy`, `updatedBy` fields on standard objects that didn't previously have them. Runs via `WorkspaceMigrationRunnerService` in a single transaction with idempotency check. **Known limitation**: assumes all standard objects exist and are valid in the target workspace. ## Add `universalIdentifier` for system fields in standard object constants - `standard-object.constant.ts` updated to include `universalIdentifier` for `createdBy`, `updatedBy`, `position`, and `searchVector` across all standard objects - `fieldManifestType.ts` updated to support the new field manifest shape ## System relation Completely removed and backfilled all `isSystem` relation to be false false As we won't require an object to have any relation system fields ## Add integration tests - New test suite `failing-sync-application-object-system-fields` covering: missing system fields, wrong field types (`id` as TEXT, `createdAt` as TEXT, `position` as TEXT), system field deletion attempts, and system field update attempts - New test utilities: `buildDefaultObjectManifest` (builds an object manifest with all 8 system fields) and `setupApplicationForSync` (centralizes application setup) - Existing successful sync test updated to verify system fields are created with correct properties ## Next step Make the builder scope the compared entity to be the currently built app + nor twenty standard app
This commit is contained in:
+10
-2
@@ -14,6 +14,7 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { aggregateOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util';
|
||||
import { crossEntityTransversalValidation } from 'src/engine/workspace-manager/workspace-migration/utils/cross-entity-transversal-validation.util';
|
||||
import { WorkspaceMigrationAgentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/agent/workspace-migration-agent-actions-builder.service';
|
||||
import { WorkspaceMigrationCommandMenuItemActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/command-menu-item/workspace-migration-command-menu-item-actions-builder.service';
|
||||
import { WorkspaceMigrationFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/workspace-migration-field-actions-builder.service';
|
||||
@@ -126,7 +127,6 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
fromToAllFlatEntityMaps,
|
||||
dependencyAllFlatEntityMaps,
|
||||
additionalCacheDataMaps,
|
||||
applicationUniversalIdentifier,
|
||||
}: WorkspaceMigrationOrchestratorBuildArgs): Promise<
|
||||
| WorkspaceMigrationOrchestratorFailedResult
|
||||
| WorkspaceMigrationOrchestratorSuccessfulResult
|
||||
@@ -994,6 +994,13 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
}
|
||||
}
|
||||
|
||||
const { objectMetadata } = crossEntityTransversalValidation({
|
||||
optimisticUniversalFlatMaps: optimisticAllFlatEntityMaps,
|
||||
orchestratorActionsReport,
|
||||
});
|
||||
|
||||
orchestratorFailureReport.objectMetadata.push(...objectMetadata);
|
||||
|
||||
const allErrors = Object.values(orchestratorFailureReport);
|
||||
|
||||
if (allErrors.some((report) => report.length > 0)) {
|
||||
@@ -1013,7 +1020,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
return {
|
||||
status: 'success',
|
||||
workspaceMigration: {
|
||||
applicationUniversalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
buildOptions.applicationUniversalIdentifier,
|
||||
actions: [
|
||||
// Object and fields and indexes
|
||||
...aggregatedOrchestratorActionsReport.index.delete,
|
||||
|
||||
+1
-1
@@ -292,10 +292,10 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
});
|
||||
|
||||
return await this.validateBuildAndRunWorkspaceMigrationFromTo({
|
||||
applicationUniversalIdentifier,
|
||||
buildOptions: {
|
||||
isSystemBuild,
|
||||
inferDeletionFromMissingEntities,
|
||||
applicationUniversalIdentifier,
|
||||
},
|
||||
fromToAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
|
||||
-1
@@ -23,7 +23,6 @@ export type WorkspaceMigrationOrchestratorBuildArgs = {
|
||||
fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps;
|
||||
dependencyAllFlatEntityMaps?: Partial<AllUniversalFlatEntityMaps>;
|
||||
additionalCacheDataMaps: WorkspaceMigrationBuilderAdditionalCacheDataMaps;
|
||||
applicationUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
export type OrchestratorFailureReport = {
|
||||
|
||||
+5
@@ -40,6 +40,7 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: { fieldMetadata: false },
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier: 'application-universal-identifier-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -67,6 +68,7 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: { fieldMetadata: true },
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier: 'application-universal-identifier-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -111,6 +113,7 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: { fieldMetadata: false },
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier: 'application-universal-identifier-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -183,6 +186,7 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: { fieldMetadata: true },
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier: 'application-universal-identifier-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -211,6 +215,7 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: { fieldMetadata: false },
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier: 'application-universal-identifier-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ export const compareTwoFlatEntity = <T extends AllMetadataName>({
|
||||
[fromUniversalFlatEntity, toUniversalFlatEntity].map(
|
||||
(universalFlatEntity) =>
|
||||
transformUniversalFlatEntityForComparison({
|
||||
metadataName,
|
||||
universalFlatEntity,
|
||||
propertiesToCompare,
|
||||
propertiesToStringify,
|
||||
|
||||
+14
@@ -8,10 +8,12 @@ import { type MetadataUniversalFlatEntityPropertiesToStringify } from 'src/engin
|
||||
export function transformUniversalFlatEntityForComparison<
|
||||
T extends AllMetadataName,
|
||||
>({
|
||||
metadataName,
|
||||
universalFlatEntity,
|
||||
propertiesToCompare,
|
||||
propertiesToStringify,
|
||||
}: {
|
||||
metadataName: T;
|
||||
universalFlatEntity: MetadataUniversalFlatEntity<T>;
|
||||
propertiesToCompare: MetadataUniversalFlatEntityPropertiesToCompare<T>[];
|
||||
propertiesToStringify: MetadataUniversalFlatEntityPropertiesToStringify<T>[];
|
||||
@@ -20,6 +22,18 @@ export function transformUniversalFlatEntityForComparison<
|
||||
(flatEntityAccumulator, propertyToCompare) => {
|
||||
const currentValue = universalFlatEntity[propertyToCompare];
|
||||
|
||||
// TODO remove once https://github.com/twentyhq/core-team-issues/issues/2227 has been resolved
|
||||
if (
|
||||
metadataName === 'index' &&
|
||||
propertyToCompare === 'universalFlatIndexFieldMetadatas' &&
|
||||
Array.isArray(currentValue)
|
||||
) {
|
||||
for (const item of currentValue) {
|
||||
delete item['createdAt'];
|
||||
delete item['updatedAt'];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
propertiesToStringify.includes(
|
||||
propertyToCompare as string as MetadataUniversalFlatEntityPropertiesToStringify<T>,
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { validateObjectMetadataSystemFieldsIntegrity } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-object-metadata-system-fields-integrity';
|
||||
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';
|
||||
|
||||
export const crossEntityTransversalValidation = ({
|
||||
optimisticUniversalFlatMaps,
|
||||
orchestratorActionsReport,
|
||||
}: {
|
||||
optimisticUniversalFlatMaps: AllUniversalFlatEntityMaps;
|
||||
orchestratorActionsReport: OrchestratorActionsReport;
|
||||
}): Pick<OrchestratorFailureReport, 'objectMetadata'> => {
|
||||
const { objectMetadata } = validateObjectMetadataSystemFieldsIntegrity({
|
||||
optimisticUniversalFlatMaps,
|
||||
orchestratorActionsReport,
|
||||
});
|
||||
|
||||
return { objectMetadata };
|
||||
};
|
||||
+1
@@ -3,4 +3,5 @@ import { type InferDeletionFromMissingEntities } from 'src/engine/workspace-mana
|
||||
export type WorkspaceMigrationBuilderOptions = {
|
||||
inferDeletionFromMissingEntities?: InferDeletionFromMissingEntities;
|
||||
isSystemBuild: boolean;
|
||||
applicationUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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 { isMorphOrRelationUniversalFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
|
||||
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
|
||||
|
||||
export const buildUniversalFlatObjectFieldByNameAndJoinColumnMaps = ({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
flatFieldMetadataMaps: AllUniversalFlatEntityMaps['flatFieldMetadataMaps'];
|
||||
flatObjectMetadata: UniversalFlatObjectMetadata;
|
||||
}) => {
|
||||
const fieldUniversalIdentifierByName: Record<string, string> = {};
|
||||
const fieldUniversalIdentifierByJoinColumnName: Record<string, string> = {};
|
||||
|
||||
const objectFields =
|
||||
findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow({
|
||||
universalIdentifiers: flatObjectMetadata.fieldUniversalIdentifiers,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
for (const field of objectFields) {
|
||||
fieldUniversalIdentifierByName[field.name] = field.universalIdentifier;
|
||||
|
||||
if (isMorphOrRelationUniversalFlatFieldMetadata(field)) {
|
||||
if (isDefined(field.universalSettings.joinColumnName)) {
|
||||
fieldUniversalIdentifierByJoinColumnName[
|
||||
field.universalSettings.joinColumnName
|
||||
] = field.universalIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fieldUniversalIdentifierByName,
|
||||
fieldUniversalIdentifierByJoinColumnName,
|
||||
};
|
||||
};
|
||||
+3
-2
@@ -8,6 +8,7 @@ import { AgentExceptionCode } from 'src/engine/metadata-modules/ai/ai-agent/agen
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { type UniversalFlatAgent } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-agent.type';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { isCallerTwentyStandardApp } from 'src/engine/metadata-modules/utils/is-caller-twenty-standard-app.util';
|
||||
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 { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
@@ -97,7 +98,7 @@ export class FlatAgentValidatorService {
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existingAgent.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
@@ -148,7 +149,7 @@ export class FlatAgentValidatorService {
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: fromFlatAgent.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
|
||||
+45
-32
@@ -14,7 +14,7 @@ import { isFlatFieldMetadataNameSyncedWithLabel } from 'src/engine/metadata-modu
|
||||
import { isMorphOrRelationUniversalFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { validateFlatFieldMetadataNameAvailability } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-flat-field-metadata-name-availability.util';
|
||||
import { validateFlatFieldMetadataName } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-flat-field-metadata-name.util';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { 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 { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
@@ -75,6 +75,31 @@ export class FlatFieldMetadataValidatorService {
|
||||
flatFieldMetadataToValidate.objectMetadataUniversalIdentifier,
|
||||
};
|
||||
|
||||
const SYSTEM_FIELD_ALLOWED_UPDATE_PROPERTIES = [
|
||||
'universalSettings',
|
||||
'isActive',
|
||||
] as const satisfies (keyof UniversalFlatFieldMetadata)[];
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
existingFlatFieldMetadataToUpdate.isSystem
|
||||
) {
|
||||
const disallowedProperties = Object.keys(flatEntityUpdate).filter(
|
||||
(property) =>
|
||||
!SYSTEM_FIELD_ALLOWED_UPDATE_PROPERTIES.includes(
|
||||
property as (typeof SYSTEM_FIELD_ALLOWED_UPDATE_PROPERTIES)[number],
|
||||
),
|
||||
);
|
||||
|
||||
if (disallowedProperties.length > 0) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message: `System fields only allow updating: ${SYSTEM_FIELD_ALLOWED_UPDATE_PROPERTIES.join(', ')}. Forbidden properties: ${disallowedProperties.join(', ')}`,
|
||||
userFriendlyMessage: msg`System fields cannot be updated`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatFieldMetadataToValidate.objectMetadataUniversalIdentifier,
|
||||
@@ -155,7 +180,7 @@ export class FlatFieldMetadataValidatorService {
|
||||
flatFieldMetadataToValidate.isLabelSyncedWithName &&
|
||||
!isFlatFieldMetadataNameSyncedWithLabel({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
isSystemBuild: buildOptions.isSystemBuild,
|
||||
buildOptions,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
@@ -205,6 +230,7 @@ export class FlatFieldMetadataValidatorService {
|
||||
flatFieldMetadataMaps: optimisticFlatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.fieldMetadata
|
||||
>): FailedFlatEntityValidation<'fieldMetadata', 'delete'> {
|
||||
@@ -244,6 +270,22 @@ export class FlatFieldMetadataValidatorService {
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const parentObjectMetadataHasBeenDeleted = !isDefined(
|
||||
relatedFlatObjectMetadata,
|
||||
);
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
flatFieldMetadataToDelete.isSystem &&
|
||||
!parentObjectMetadataHasBeenDeleted
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message: 'System fields cannot be deleted',
|
||||
userFriendlyMessage: msg`System fields cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(relatedFlatObjectMetadata) &&
|
||||
relatedFlatObjectMetadata.labelIdentifierFieldMetadataUniversalIdentifier ===
|
||||
@@ -257,35 +299,6 @@ export class FlatFieldMetadataValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const relationTargetObjectMetadataHasBeenDeleted =
|
||||
isMorphOrRelationUniversalFlatFieldMetadata(flatFieldMetadataToDelete) &&
|
||||
!isDefined(
|
||||
findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatFieldMetadataToDelete.relationTargetObjectMetadataUniversalIdentifier,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
}),
|
||||
);
|
||||
const parentObjectMetadataHasBeenDeleted = !isDefined(
|
||||
findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatFieldMetadataToDelete.objectMetadataUniversalIdentifier,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
belongsToTwentyStandardApp(flatFieldMetadataToDelete) &&
|
||||
!relationTargetObjectMetadataHasBeenDeleted &&
|
||||
!parentObjectMetadataHasBeenDeleted
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
message: "Standard Fields can't be deleted",
|
||||
userFriendlyMessage: msg`Standard fields cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
@@ -365,7 +378,7 @@ export class FlatFieldMetadataValidatorService {
|
||||
flatFieldMetadataToValidate.isLabelSyncedWithName &&
|
||||
!isFlatFieldMetadataNameSyncedWithLabel({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
isSystemBuild: buildOptions.isSystemBuild,
|
||||
buildOptions,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
|
||||
+18
-1
@@ -9,6 +9,7 @@ import { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modul
|
||||
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';
|
||||
import { isCallerTwentyStandardApp } from 'src/engine/metadata-modules/utils/is-caller-twenty-standard-app.util';
|
||||
import { 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 { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
@@ -61,6 +62,14 @@ export class FlatObjectMetadataValidatorService {
|
||||
nameSingular: existingFlatObjectMetadata.nameSingular,
|
||||
};
|
||||
|
||||
if (!buildOptions.isSystemBuild && existingFlatObjectMetadata.isSystem) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`System objects cannot be updated`,
|
||||
userFriendlyMessage: msg`System objects cannot be updated`,
|
||||
});
|
||||
}
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateFlatObjectMetadataNameAndLabels({
|
||||
optimisticUniversalFlatObjectMetadataMaps:
|
||||
@@ -139,8 +148,16 @@ export class FlatObjectMetadataValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
if (!buildOptions.isSystemBuild && flatObjectMetadataToDelete.isSystem) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`System objects cannot be deleted`,
|
||||
userFriendlyMessage: msg`System objects cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp(flatObjectMetadataToDelete)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@ import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules
|
||||
import { SkillExceptionCode } from 'src/engine/metadata-modules/skill/skill.exception';
|
||||
import { type UniversalFlatSkill } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-skill.type';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { isCallerTwentyStandardApp } from 'src/engine/metadata-modules/utils/is-caller-twenty-standard-app.util';
|
||||
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 { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
@@ -90,7 +91,7 @@ export class FlatSkillValidatorService {
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existingSkill.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
@@ -153,7 +154,7 @@ export class FlatSkillValidatorService {
|
||||
});
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
isTwentyStandardSkill &&
|
||||
hasNonIsActiveUpdates
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user