[REQUIRES_CACHE_FLUSH_FOR_FIELD_AND_OBJECT]FlatFieldMetadata and FlatObjectMetadata required universal (#17557)
# Introduction
In this PR we're migrating both the `field` and `object` metadata to be
using the new `FlatEntityFromV2` that requires all the
`UniversalFlatEntityExtraProperties` to be spread at the flat entity
root.
This means that we have to update all of their flat declaration
This type swap allows to isole a specific entity migration into his own
type scope and avoid to have everything handled at once
```ts
/**
* Currently under migration but aims to replace FlatEntity afterwards
*/
export type FlatEntityFromV2<
TEntity,
TMetadataName extends AllMetadataName | undefined = undefined,
TInnerFlatEntity extends { __universal?: unknown } = FlatEntityFrom<
TEntity,
TMetadataName
>,
> = Omit<TInnerFlatEntity, '__universal'> & TInnerFlatEntity['__universal'];
```
## Impact
Both object and field:
- Create input transpilation utils
- from entity to flat tools
- mocks
## Note
Removed from the universal extra properties the jsonb properties that do
not contain a serialized
## Next
Next step is to incrementally make the builder and runner expect
`UniversalFlatEntity` for both of these metadata
This way we will be able to fully migrate an entity e2e typesafely
This commit is contained in:
+3
@@ -10,6 +10,7 @@ export enum ObjectMetadataExceptionCode {
|
||||
INVALID_OBJECT_INPUT = 'INVALID_OBJECT_INPUT',
|
||||
OBJECT_MUTATION_NOT_ALLOWED = 'OBJECT_MUTATION_NOT_ALLOWED',
|
||||
OBJECT_ALREADY_EXISTS = 'OBJECT_ALREADY_EXISTS',
|
||||
APPLICATION_NOT_FOUND = 'APPLICATION_NOT_FOUND',
|
||||
MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD = 'MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD',
|
||||
INVALID_ORM_OUTPUT = 'INVALID_ORM_OUTPUT',
|
||||
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
|
||||
@@ -28,6 +29,8 @@ const getObjectMetadataExceptionUserFriendlyMessage = (
|
||||
return msg`This object cannot be modified.`;
|
||||
case ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS:
|
||||
return msg`An object with this name already exists.`;
|
||||
case ObjectMetadataExceptionCode.APPLICATION_NOT_FOUND:
|
||||
return msg`Application not found.`;
|
||||
case ObjectMetadataExceptionCode.MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD:
|
||||
return msg`Custom object is missing a label identifier field.`;
|
||||
case ObjectMetadataExceptionCode.INVALID_ORM_OUTPUT:
|
||||
|
||||
+14
-2
@@ -331,11 +331,24 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
const {
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
featureFlagsMap: existingFeatureFlagsMap,
|
||||
flatApplicationMaps,
|
||||
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatObjectMetadataMaps',
|
||||
'featureFlagsMap',
|
||||
'flatApplicationMaps',
|
||||
]);
|
||||
|
||||
const ownerFlatApplication = isDefined(applicationId)
|
||||
? flatApplicationMaps.byId[applicationId]
|
||||
: workspaceCustomFlatApplication;
|
||||
|
||||
if (!isDefined(ownerFlatApplication)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Could not find related application ${applicationId}`,
|
||||
ObjectMetadataExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
flatObjectMetadataToCreate,
|
||||
flatIndexMetadataToCreate,
|
||||
@@ -344,8 +357,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
} = fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate({
|
||||
createObjectInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId:
|
||||
applicationId ?? workspaceCustomFlatApplication.id,
|
||||
flatApplication: ownerFlatApplication,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
existingFeatureFlagsMap,
|
||||
});
|
||||
|
||||
+103
-2
@@ -15,7 +15,13 @@ import {
|
||||
type BuildDefaultFlatFieldMetadataForCustomObjectArgs = {
|
||||
workspaceId: string;
|
||||
flatObjectMetadata: NonNullableRequired<
|
||||
Pick<FlatObjectMetadata, 'id' | 'applicationId'>
|
||||
Pick<
|
||||
FlatObjectMetadata,
|
||||
| 'id'
|
||||
| 'applicationId'
|
||||
| 'universalIdentifier'
|
||||
| 'applicationUniversalIdentifier'
|
||||
>
|
||||
>;
|
||||
skipNameField?: boolean;
|
||||
};
|
||||
@@ -26,7 +32,12 @@ export type DefaultFlatFieldForCustomObjectMaps = ReturnType<
|
||||
// This could be replaced totally by an import schema + its transpilation when it's ready
|
||||
export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
workspaceId,
|
||||
flatObjectMetadata: { id: objectMetadataId, applicationId },
|
||||
flatObjectMetadata: {
|
||||
id: objectMetadataId,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
universalIdentifier: objectMetadataUniversalIdentifier,
|
||||
},
|
||||
skipNameField = false,
|
||||
}: BuildDefaultFlatFieldMetadataForCustomObjectArgs) => {
|
||||
const createdAt = new Date().toISOString();
|
||||
@@ -65,6 +76,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const nameFieldId = skipNameField ? null : v4();
|
||||
@@ -105,6 +126,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const createdAtFieldId = v4();
|
||||
@@ -142,6 +173,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const updatedAtFieldId = v4();
|
||||
@@ -179,6 +220,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const deletedAtFieldId = v4();
|
||||
@@ -216,6 +267,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const createdByFieldId = v4();
|
||||
@@ -252,6 +313,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const updatedByFieldId = v4();
|
||||
@@ -288,6 +359,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const positionFieldId = v4();
|
||||
@@ -325,6 +406,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const searchVectorFieldId = v4();
|
||||
@@ -367,6 +458,16 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
},
|
||||
morphId: null,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
+4
-3
@@ -5,6 +5,7 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
@@ -53,7 +54,7 @@ export type BuildDefaultRelationFieldsForCustomObjectArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
workspaceId: string;
|
||||
sourceFlatObjectMetadata: FlatObjectMetadata;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
|
||||
type SourceAndTargetFlatFieldMetadatasRecord = {
|
||||
@@ -71,7 +72,7 @@ export const buildDefaultRelationFlatFieldMetadatasForCustomObject = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceFlatObjectMetadata,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: BuildDefaultRelationFieldsForCustomObjectArgs): SourceAndTargetFlatFieldMetadatasRecord => {
|
||||
const objectIdByNameSingular = Object.values(
|
||||
existingFlatObjectMetadataMaps.byId,
|
||||
@@ -156,7 +157,7 @@ export const buildDefaultRelationFlatFieldMetadatasForCustomObject = ({
|
||||
? FieldMetadataType.MORPH_RELATION
|
||||
: FieldMetadataType.RELATION,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
sourceFlatObjectMetadataJoinColumnName: joinColumnName,
|
||||
morphId,
|
||||
targetFieldName: fieldName,
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ export const objectMetadataGraphqlApiExceptionHandler = (
|
||||
case ObjectMetadataExceptionCode.INVALID_ORM_OUTPUT:
|
||||
throw new InternalServerError(error);
|
||||
case ObjectMetadataExceptionCode.MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD:
|
||||
case ObjectMetadataExceptionCode.APPLICATION_NOT_FOUND:
|
||||
throw error;
|
||||
default: {
|
||||
return assertUnreachable(error.code);
|
||||
|
||||
Reference in New Issue
Block a user