[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:
+40
-33
@@ -132,36 +132,43 @@ export const mockPersonFlatObjectMetadataMaps = (
|
||||
|
||||
export const mockPersonFlatObjectMetadata = (
|
||||
duplicateCriteria: WorkspaceEntityDuplicateCriteria[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: objectMetadataId,
|
||||
icon: 'Icon123',
|
||||
standardId: '',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
targetTableName: 'person',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: true,
|
||||
isSearchable: true,
|
||||
duplicateCriteria: duplicateCriteria,
|
||||
labelIdentifierFieldMetadataId: '',
|
||||
imageIdentifierFieldMetadataId: '',
|
||||
workspaceId,
|
||||
universalIdentifier: objectMetadataId,
|
||||
indexMetadataIds: [],
|
||||
fieldIds: mockFieldMetadatas.map((field) => field.id),
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
}) as FlatObjectMetadata;
|
||||
): FlatObjectMetadata => ({
|
||||
id: objectMetadataId,
|
||||
icon: 'Icon123',
|
||||
standardId: '',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
targetTableName: 'person',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: true,
|
||||
isSearchable: true,
|
||||
duplicateCriteria: duplicateCriteria,
|
||||
labelIdentifierFieldMetadataId: '',
|
||||
imageIdentifierFieldMetadataId: '',
|
||||
workspaceId,
|
||||
universalIdentifier: objectMetadataId,
|
||||
indexMetadataIds: [],
|
||||
fieldIds: mockFieldMetadatas.map((field) => field.id),
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: mockFieldMetadatas.map(
|
||||
(field) => field.universalIdentifier,
|
||||
),
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
});
|
||||
|
||||
+8
-2
@@ -3,7 +3,7 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
const mockObjectMetadata = {
|
||||
const mockObjectMetadata: FlatObjectMetadata = {
|
||||
id: '1',
|
||||
icon: 'Icon123',
|
||||
nameSingular: 'Object',
|
||||
@@ -34,7 +34,13 @@ const mockObjectMetadata = {
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
} as FlatObjectMetadata;
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: [],
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
};
|
||||
|
||||
const mockFlatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata> = {
|
||||
byId: {},
|
||||
|
||||
+3
@@ -14,6 +14,7 @@ export const FieldMetadataExceptionCode = appendCommonExceptionCode({
|
||||
FIELD_MUTATION_NOT_ALLOWED: 'FIELD_MUTATION_NOT_ALLOWED',
|
||||
FIELD_ALREADY_EXISTS: 'FIELD_ALREADY_EXISTS',
|
||||
OBJECT_METADATA_NOT_FOUND: 'OBJECT_METADATA_NOT_FOUND',
|
||||
APPLICATION_NOT_FOUND: 'APPLICATION_NOT_FOUND',
|
||||
FIELD_METADATA_RELATION_NOT_ENABLED: 'FIELD_METADATA_RELATION_NOT_ENABLED',
|
||||
FIELD_METADATA_RELATION_MALFORMED: 'FIELD_METADATA_RELATION_MALFORMED',
|
||||
LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND:
|
||||
@@ -43,6 +44,8 @@ const getFieldMetadataExceptionUserFriendlyMessage = (
|
||||
return msg`A field with this name already exists.`;
|
||||
case FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
return msg`Object not found.`;
|
||||
case FieldMetadataExceptionCode.APPLICATION_NOT_FOUND:
|
||||
return msg`Application not found.`;
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_NOT_ENABLED:
|
||||
return msg`Relation is not enabled for this field.`;
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED:
|
||||
|
||||
+2
@@ -31,6 +31,7 @@ import { ViewModule } from 'src/engine/metadata-modules/view/view.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
|
||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||
|
||||
@@ -62,6 +63,7 @@ import { UpdateFieldInput } from './dtos/update-field.input';
|
||||
FlatFieldMetadataModule,
|
||||
IndexMetadataModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceCacheModule,
|
||||
],
|
||||
services: [FieldMetadataService],
|
||||
resolvers: [
|
||||
|
||||
+24
-9
@@ -22,6 +22,7 @@ import { fromCreateFieldInputToFlatFieldMetadatasToCreate } from 'src/engine/met
|
||||
import { fromDeleteFieldInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util';
|
||||
import { fromUpdateFieldInputToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util';
|
||||
import { throwOnFieldInputTranspilationsError } from 'src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { EMPTY_ORCHESTRATOR_FAILURE_REPORT } from 'src/engine/workspace-manager/workspace-migration/constant/empty-orchestrator-failure-report.constant';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
@@ -34,6 +35,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
) {
|
||||
super(fieldMetadataRepository);
|
||||
}
|
||||
@@ -180,7 +182,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
isSystemBuild,
|
||||
});
|
||||
|
||||
@@ -309,13 +311,26 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
},
|
||||
);
|
||||
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
const {
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatApplicationMaps,
|
||||
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatApplicationMaps',
|
||||
]);
|
||||
|
||||
const ownerFlatApplication = isDefined(applicationId)
|
||||
? flatApplicationMaps.byId[applicationId]
|
||||
: workspaceCustomFlatApplication;
|
||||
|
||||
if (!isDefined(ownerFlatApplication)) {
|
||||
throw new FieldMetadataException(
|
||||
`Could not find related application ${applicationId}`,
|
||||
FieldMetadataExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const allTranspiledTranspilationInputs: Awaited<
|
||||
ReturnType<typeof fromCreateFieldInputToFlatFieldMetadatasToCreate>
|
||||
@@ -325,10 +340,10 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
allTranspiledTranspilationInputs.push(
|
||||
await fromCreateFieldInputToFlatFieldMetadatasToCreate({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId:
|
||||
applicationId ?? workspaceCustomFlatApplication.id,
|
||||
flatApplication: ownerFlatApplication,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ export const fieldMetadataGraphqlApiExceptionHandler = (
|
||||
case FieldMetadataExceptionCode.FIELD_ALREADY_EXISTS:
|
||||
throw new ConflictError(error);
|
||||
case FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
case FieldMetadataExceptionCode.APPLICATION_NOT_FOUND:
|
||||
case FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_NOT_ENABLED:
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED:
|
||||
|
||||
-2
@@ -43,8 +43,6 @@ export const transformAgentEntityToFlatAgent = ({
|
||||
__universal: {
|
||||
universalIdentifier: agentEntity.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
responseFormat: agentEntity.responseFormat,
|
||||
modelConfiguration: agentEntity.modelConfiguration,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import { type Expect } from 'twenty-shared/testing';
|
||||
import { type AllFlatEntityTypesByMetadataName } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name';
|
||||
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type WorkspaceMigrationActionType } from 'src/engine/metadata-modules/flat-entity/types/metadata-workspace-migration-action.type';
|
||||
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
|
||||
import { type WorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/workspace-migration-action-common';
|
||||
|
||||
type ExpectedGenericFlatEntityInformation = {
|
||||
@@ -13,6 +14,7 @@ type ExpectedGenericFlatEntityInformation = {
|
||||
| WorkspaceMigrationAction[];
|
||||
};
|
||||
flatEntity: SyncableFlatEntity;
|
||||
universalFlatEntity: UniversalSyncableFlatEntity;
|
||||
};
|
||||
|
||||
type ExpectedGenericAllFlatEntityInformationByMetadataEngine = {
|
||||
|
||||
+52
-2
@@ -1,10 +1,12 @@
|
||||
import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { type CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatFrontComponent } from 'src/engine/metadata-modules/flat-front-component/types/flat-front-component.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatWebhook } from 'src/engine/metadata-modules/flat-webhook/types/flat-webhook.type';
|
||||
import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
|
||||
@@ -18,10 +20,30 @@ import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { type FlatWebhook } from 'src/engine/metadata-modules/flat-webhook/types/flat-webhook.type';
|
||||
import { type FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
|
||||
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { type LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { type NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { type PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type';
|
||||
import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type';
|
||||
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { type SkillEntity } from 'src/engine/metadata-modules/skill/entities/skill.entity';
|
||||
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { type ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
|
||||
import { type ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
import { type WebhookEntity } from 'src/engine/metadata-modules/webhook/entities/webhook.entity';
|
||||
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
|
||||
import {
|
||||
type CreateAgentAction,
|
||||
type DeleteAgentAction,
|
||||
@@ -141,6 +163,10 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteFieldAction;
|
||||
};
|
||||
flatEntity: FlatFieldMetadata;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<
|
||||
FieldMetadataEntity,
|
||||
'fieldMetadata'
|
||||
>;
|
||||
entity: MetadataEntity<'fieldMetadata'>;
|
||||
};
|
||||
objectMetadata: {
|
||||
@@ -150,6 +176,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteObjectAction;
|
||||
};
|
||||
flatEntity: FlatObjectMetadata;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ObjectMetadataEntity>;
|
||||
entity: MetadataEntity<'objectMetadata'>;
|
||||
};
|
||||
view: {
|
||||
@@ -159,6 +186,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteViewAction;
|
||||
};
|
||||
flatEntity: FlatView;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ViewEntity>;
|
||||
entity: MetadataEntity<'view'>;
|
||||
};
|
||||
viewField: {
|
||||
@@ -168,6 +196,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteViewFieldAction;
|
||||
};
|
||||
flatEntity: FlatViewField;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ViewFieldEntity>;
|
||||
entity: MetadataEntity<'viewField'>;
|
||||
};
|
||||
viewGroup: {
|
||||
@@ -177,6 +206,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteViewGroupAction;
|
||||
};
|
||||
flatEntity: FlatViewGroup;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ViewGroupEntity>;
|
||||
entity: MetadataEntity<'viewGroup'>;
|
||||
};
|
||||
rowLevelPermissionPredicate: {
|
||||
@@ -186,6 +216,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteRowLevelPermissionPredicateAction;
|
||||
};
|
||||
flatEntity: FlatRowLevelPermissionPredicate;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<RowLevelPermissionPredicateEntity>;
|
||||
entity: MetadataEntity<'rowLevelPermissionPredicate'>;
|
||||
};
|
||||
rowLevelPermissionPredicateGroup: {
|
||||
@@ -195,6 +226,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteRowLevelPermissionPredicateGroupAction;
|
||||
};
|
||||
flatEntity: FlatRowLevelPermissionPredicateGroup;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<RowLevelPermissionPredicateGroupEntity>;
|
||||
entity: MetadataEntity<'rowLevelPermissionPredicateGroup'>;
|
||||
};
|
||||
viewFilterGroup: {
|
||||
@@ -204,6 +236,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteViewFilterGroupAction;
|
||||
};
|
||||
flatEntity: FlatViewFilterGroup;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ViewFilterGroupEntity>;
|
||||
entity: MetadataEntity<'viewFilterGroup'>;
|
||||
};
|
||||
index: {
|
||||
@@ -213,6 +246,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteIndexAction;
|
||||
};
|
||||
flatEntity: FlatIndexMetadata;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<IndexMetadataEntity>;
|
||||
entity: MetadataEntity<'index'>;
|
||||
};
|
||||
logicFunction: {
|
||||
@@ -222,6 +256,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteLogicFunctionAction;
|
||||
};
|
||||
flatEntity: FlatLogicFunction;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<LogicFunctionEntity>;
|
||||
entity: MetadataEntity<'logicFunction'>;
|
||||
};
|
||||
viewFilter: {
|
||||
@@ -231,6 +266,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteViewFilterAction;
|
||||
};
|
||||
flatEntity: FlatViewFilter;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<ViewFilterEntity>;
|
||||
entity: MetadataEntity<'viewFilter'>;
|
||||
};
|
||||
role: {
|
||||
@@ -240,6 +276,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteRoleAction;
|
||||
};
|
||||
flatEntity: FlatRole;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<RoleEntity>;
|
||||
entity: MetadataEntity<'role'>;
|
||||
};
|
||||
roleTarget: {
|
||||
@@ -249,6 +286,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteRoleTargetAction;
|
||||
};
|
||||
flatEntity: FlatRoleTarget;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<RoleTargetEntity>;
|
||||
entity: MetadataEntity<'roleTarget'>;
|
||||
};
|
||||
agent: {
|
||||
@@ -258,6 +296,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteAgentAction;
|
||||
};
|
||||
flatEntity: FlatAgent;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<AgentEntity>;
|
||||
entity: MetadataEntity<'agent'>;
|
||||
};
|
||||
skill: {
|
||||
@@ -267,6 +306,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteSkillAction;
|
||||
};
|
||||
flatEntity: FlatSkill;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<SkillEntity>;
|
||||
entity: MetadataEntity<'skill'>;
|
||||
};
|
||||
commandMenuItem: {
|
||||
@@ -276,6 +316,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteCommandMenuItemAction;
|
||||
};
|
||||
flatEntity: FlatCommandMenuItem;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<CommandMenuItemEntity>;
|
||||
entity: MetadataEntity<'commandMenuItem'>;
|
||||
};
|
||||
navigationMenuItem: {
|
||||
@@ -285,6 +326,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteNavigationMenuItemAction;
|
||||
};
|
||||
flatEntity: FlatNavigationMenuItem;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<NavigationMenuItemEntity>;
|
||||
entity: NavigationMenuItemEntity;
|
||||
};
|
||||
pageLayout: {
|
||||
@@ -294,6 +336,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeletePageLayoutAction;
|
||||
};
|
||||
flatEntity: FlatPageLayout;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<PageLayoutEntity>;
|
||||
entity: MetadataEntity<'pageLayout'>;
|
||||
};
|
||||
pageLayoutWidget: {
|
||||
@@ -303,6 +346,10 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeletePageLayoutWidgetAction;
|
||||
};
|
||||
flatEntity: FlatPageLayoutWidget;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<
|
||||
PageLayoutWidgetEntity,
|
||||
'pageLayoutWidget'
|
||||
>;
|
||||
entity: MetadataEntity<'pageLayoutWidget'>;
|
||||
};
|
||||
pageLayoutTab: {
|
||||
@@ -312,6 +359,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeletePageLayoutTabAction;
|
||||
};
|
||||
flatEntity: FlatPageLayoutTab;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<PageLayoutTabEntity>;
|
||||
entity: MetadataEntity<'pageLayoutTab'>;
|
||||
};
|
||||
frontComponent: {
|
||||
@@ -321,6 +369,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteFrontComponentAction;
|
||||
};
|
||||
flatEntity: FlatFrontComponent;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<FrontComponentEntity>;
|
||||
entity: MetadataEntity<'frontComponent'>;
|
||||
};
|
||||
webhook: {
|
||||
@@ -330,6 +379,7 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
delete: DeleteWebhookAction;
|
||||
};
|
||||
flatEntity: FlatWebhook;
|
||||
universalFlatEntity: UniversalFlatEntityFrom<WebhookEntity>;
|
||||
entity: MetadataEntity<'webhook'>;
|
||||
};
|
||||
};
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
+1
@@ -16,6 +16,7 @@ export type SyncableFlatEntity = Omit<
|
||||
|
||||
export type FlatEntityFrom<
|
||||
TEntity,
|
||||
// Required to be passed for narrowed type
|
||||
TMetadataName extends AllMetadataName | undefined = undefined,
|
||||
> = Omit<
|
||||
TEntity,
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type AllFlatEntityTypesByMetadataName } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name';
|
||||
|
||||
export type MetadataUniversalFlatEntity<T extends AllMetadataName> =
|
||||
AllFlatEntityTypesByMetadataName[T]['universalFlatEntity'];
|
||||
+10
-2
@@ -45,8 +45,16 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
applicationId: faker.string.uuid(),
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
__universal: {} as any,
|
||||
applicationUniversalIdentifier: faker.string.uuid(),
|
||||
objectMetadataUniversalIdentifier: faker.string.uuid(),
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
+10
-1
@@ -65,6 +65,15 @@ export const getRelationTargetFlatFieldMetadataMock = ({
|
||||
defaultValue: null,
|
||||
options: null,
|
||||
applicationId: faker.string.uuid(),
|
||||
__universal: undefined,
|
||||
applicationUniversalIdentifier: faker.string.uuid(),
|
||||
objectMetadataUniversalIdentifier: faker.string.uuid(),
|
||||
relationTargetObjectMetadataUniversalIdentifier: faker.string.uuid(),
|
||||
relationTargetFieldMetadataUniversalIdentifier: faker.string.uuid(),
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: settings,
|
||||
};
|
||||
};
|
||||
|
||||
+53
-1
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test suite Failure cases should fail when morphRelationsCreationPayload has different relation types 1`] = `
|
||||
{
|
||||
@@ -117,7 +117,9 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"applicationId": Any<String>,
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": Any<String>,
|
||||
"defaultValue": null,
|
||||
"description": "new field description",
|
||||
@@ -131,14 +133,19 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "newFieldLabel",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": Any<String>,
|
||||
"name": "newFieldPets",
|
||||
"objectMetadataId": Any<String>,
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataId": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"settings": {
|
||||
"relationType": "ONE_TO_MANY",
|
||||
},
|
||||
@@ -146,14 +153,21 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY",
|
||||
},
|
||||
"updatedAt": Any<String>,
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
"applicationId": Any<String>,
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": Any<String>,
|
||||
"defaultValue": null,
|
||||
"description": "new field description",
|
||||
@@ -167,14 +181,19 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "Pet",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "pet",
|
||||
"objectMetadataId": Any<String>,
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataId": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"settings": {
|
||||
"joinColumnName": "petId",
|
||||
"onDelete": "SET_NULL",
|
||||
@@ -184,14 +203,23 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
"joinColumnName": "petId",
|
||||
"onDelete": "SET_NULL",
|
||||
"relationType": "MANY_TO_ONE",
|
||||
},
|
||||
"updatedAt": Any<String>,
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
"applicationId": Any<String>,
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": Any<String>,
|
||||
"defaultValue": null,
|
||||
"description": "new field description",
|
||||
@@ -205,14 +233,19 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "newFieldLabel",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": Any<String>,
|
||||
"name": "newFieldCompanies",
|
||||
"objectMetadataId": Any<String>,
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataId": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"settings": {
|
||||
"relationType": "ONE_TO_MANY",
|
||||
},
|
||||
@@ -220,14 +253,21 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY",
|
||||
},
|
||||
"updatedAt": Any<String>,
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
"applicationId": Any<String>,
|
||||
"applicationUniversalIdentifier": Any<String>,
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": Any<String>,
|
||||
"defaultValue": null,
|
||||
"description": "new field description",
|
||||
@@ -241,14 +281,19 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "Company",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "company",
|
||||
"objectMetadataId": Any<String>,
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataId": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"settings": {
|
||||
"joinColumnName": "companyId",
|
||||
"onDelete": "SET_NULL",
|
||||
@@ -258,9 +303,16 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
"joinColumnName": "companyId",
|
||||
"onDelete": "SET_NULL",
|
||||
"relationType": "MANY_TO_ONE",
|
||||
},
|
||||
"updatedAt": Any<String>,
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
],
|
||||
|
||||
+38
-14
@@ -5,8 +5,11 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
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 { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
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 { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import {
|
||||
fromCreateFieldInputToFlatFieldMetadatasToCreate,
|
||||
type FromCreateFieldInputToFlatObjectMetadataArgs,
|
||||
@@ -16,6 +19,24 @@ import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/fla
|
||||
import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock';
|
||||
import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock';
|
||||
|
||||
const MOCK_FLAT_APPLICATION: FlatApplication = {
|
||||
id: '20202020-81ee-42da-a281-668632f32fe7',
|
||||
universalIdentifier: '20202020-81ee-42da-a281-668632f32fe7',
|
||||
workspaceId: 'mock-workspace-id',
|
||||
name: 'Workspace Custom Application',
|
||||
description: null,
|
||||
version: null,
|
||||
sourceType: 'local',
|
||||
sourcePath: '',
|
||||
logicFunctionLayerId: null,
|
||||
defaultLogicFunctionRoleId: null,
|
||||
defaultLogicFunctionRole: null,
|
||||
canBeUninstalled: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
const flatObjectMetadataMaps = [
|
||||
COMPANY_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
@@ -27,6 +48,9 @@ const flatObjectMetadataMaps = [
|
||||
});
|
||||
}, createEmptyFlatEntityMaps());
|
||||
|
||||
const emptyFlatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata> =
|
||||
createEmptyFlatEntityMaps();
|
||||
|
||||
type TestCase = EachTestingContext<{
|
||||
input: FromCreateFieldInputToFlatObjectMetadataArgs;
|
||||
expected: 'success' | 'fail';
|
||||
@@ -42,8 +66,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
'should create morph relation field metadata with valid input on rocket object to pet object',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -67,6 +90,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'success',
|
||||
},
|
||||
@@ -102,8 +126,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
title: 'should fail when morphRelationsCreationPayload is missing',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -114,6 +137,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
@@ -122,8 +146,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
title: 'should fail when morphRelationsCreationPayload is empty array',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -134,6 +157,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
@@ -143,8 +167,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
'should fail when morphRelationsCreationPayload has different relation types',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -168,6 +191,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
@@ -177,8 +201,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
'should fail when morphRelationsCreationPayload has several references to same object metadata',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -202,6 +225,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
@@ -211,8 +235,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
'should fail when morphRelationsCreationPayload has invalid relation payload',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -229,6 +252,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
@@ -237,8 +261,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
title: 'should fail when target object metadata is not found',
|
||||
context: {
|
||||
input: {
|
||||
workspaceCustomApplicationId:
|
||||
'20202020-81ee-42da-a281-668632f32fe7',
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'newField',
|
||||
label: 'newFieldLabel',
|
||||
@@ -256,6 +279,7 @@ describe('fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test s
|
||||
},
|
||||
workspaceId: mockWorkspaceId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadataMaps: emptyFlatFieldMetadataMaps,
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
|
||||
+45
@@ -71,3 +71,48 @@ type FlatTransformationAssertions = [
|
||||
Expect<HasAllProperties<FlatFieldMetadata, DatePropertiesCastToString>>,
|
||||
Expect<HasAllProperties<FlatFieldMetadata, OneToManyRelationIdArrays>>,
|
||||
];
|
||||
|
||||
// Any narrowed flatFieldMetadata type should be assignable to non narrowed flatFieldMetadata
|
||||
type AbstractFlatFieldMetadata = FlatFieldMetadata<FieldMetadataType>;
|
||||
|
||||
const _assertion: Record<string, AbstractFlatFieldMetadata> = {
|
||||
// Primitive types
|
||||
uuid: {} as FlatFieldMetadata<FieldMetadataType.UUID>,
|
||||
text: {} as FlatFieldMetadata<FieldMetadataType.TEXT>,
|
||||
number: {} as FlatFieldMetadata<FieldMetadataType.NUMBER>,
|
||||
boolean: {} as FlatFieldMetadata<FieldMetadataType.BOOLEAN>,
|
||||
numeric: {} as FlatFieldMetadata<FieldMetadataType.NUMERIC>,
|
||||
position: {} as FlatFieldMetadata<FieldMetadataType.POSITION>,
|
||||
|
||||
// Date types
|
||||
date: {} as FlatFieldMetadata<FieldMetadataType.DATE>,
|
||||
dateTime: {} as FlatFieldMetadata<FieldMetadataType.DATE_TIME>,
|
||||
|
||||
// Complex types
|
||||
currency: {} as FlatFieldMetadata<FieldMetadataType.CURRENCY>,
|
||||
fullName: {} as FlatFieldMetadata<FieldMetadataType.FULL_NAME>,
|
||||
address: {} as FlatFieldMetadata<FieldMetadataType.ADDRESS>,
|
||||
links: {} as FlatFieldMetadata<FieldMetadataType.LINKS>,
|
||||
emails: {} as FlatFieldMetadata<FieldMetadataType.EMAILS>,
|
||||
phones: {} as FlatFieldMetadata<FieldMetadataType.PHONES>,
|
||||
actor: {} as FlatFieldMetadata<FieldMetadataType.ACTOR>,
|
||||
|
||||
// Select types
|
||||
rating: {} as FlatFieldMetadata<FieldMetadataType.RATING>,
|
||||
select: {} as FlatFieldMetadata<FieldMetadataType.SELECT>,
|
||||
multiSelect: {} as FlatFieldMetadata<FieldMetadataType.MULTI_SELECT>,
|
||||
|
||||
// JSON/Array types
|
||||
rawJson: {} as FlatFieldMetadata<FieldMetadataType.RAW_JSON>,
|
||||
array: {} as FlatFieldMetadata<FieldMetadataType.ARRAY>,
|
||||
richText: {} as FlatFieldMetadata<FieldMetadataType.RICH_TEXT>,
|
||||
richTextV2: {} as FlatFieldMetadata<FieldMetadataType.RICH_TEXT_V2>,
|
||||
|
||||
// Relation types
|
||||
relation: {} as FlatFieldMetadata<FieldMetadataType.RELATION>,
|
||||
morphRelation: {} as FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>,
|
||||
|
||||
// Special types
|
||||
files: {} as FlatFieldMetadata<FieldMetadataType.FILES>,
|
||||
tsVector: {} as FlatFieldMetadata<FieldMetadataType.TS_VECTOR>,
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type FlatEntityFromV2 } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from-v2.type';
|
||||
|
||||
export type FlatFieldMetadata<T extends FieldMetadataType = FieldMetadataType> =
|
||||
FlatEntityFrom<
|
||||
FlatEntityFromV2<
|
||||
Omit<
|
||||
FieldMetadataEntity<T>,
|
||||
'relationTargetFieldMetadata' | 'relationTargetObjectMetadata'
|
||||
|
||||
+25
-8
@@ -6,6 +6,7 @@ import {
|
||||
RelationType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import {
|
||||
generateMorphOrRelationFlatFieldMetadataPair,
|
||||
type SourceTargetMorphOrRelationFlatFieldAndFlatIndex,
|
||||
@@ -13,6 +14,24 @@ import {
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock';
|
||||
|
||||
const MOCK_FLAT_APPLICATION: FlatApplication = {
|
||||
id: '20202020-81ee-42da-a281-668632f32fe7',
|
||||
universalIdentifier: '20202020-81ee-42da-a281-668632f32fe7',
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
name: 'Workspace Custom Application',
|
||||
description: null,
|
||||
version: null,
|
||||
sourceType: 'local',
|
||||
sourcePath: '',
|
||||
logicFunctionLayerId: null,
|
||||
defaultLogicFunctionRoleId: null,
|
||||
defaultLogicFunctionRole: null,
|
||||
canBeUninstalled: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
type GenerateMorphOrRelationFlatFieldMetadataPairTestInput = Parameters<
|
||||
typeof generateMorphOrRelationFlatFieldMetadataPair
|
||||
>[0];
|
||||
@@ -28,8 +47,6 @@ type TestCase = EachTestingContext<{
|
||||
|
||||
describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => {
|
||||
const mockWorkspaceId = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
||||
const mockWorkspaceCustomApplicationId =
|
||||
'20202020-81ee-42da-a281-668632f32fe7';
|
||||
|
||||
describe('Success cases', () => {
|
||||
const testCases: TestCase[] = [
|
||||
@@ -43,7 +60,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'petId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'pets',
|
||||
label: 'Pets',
|
||||
@@ -79,7 +96,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'petId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'pets',
|
||||
label: 'Pets',
|
||||
@@ -115,7 +132,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'petId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
createFieldInput: {
|
||||
name: 'pets',
|
||||
label: 'Pets',
|
||||
@@ -151,7 +168,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'targetPetId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1',
|
||||
createFieldInput: {
|
||||
name: 'targetPet',
|
||||
@@ -188,7 +205,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'targetPetId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1',
|
||||
createFieldInput: {
|
||||
name: 'targetPet',
|
||||
@@ -225,7 +242,7 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () =>
|
||||
targetFlatFieldMetadataType: FieldMetadataType.MORPH_RELATION,
|
||||
sourceFlatObjectMetadataJoinColumnName: 'targetPetId',
|
||||
workspaceId: mockWorkspaceId,
|
||||
workspaceCustomApplicationId: mockWorkspaceCustomApplicationId,
|
||||
flatApplication: MOCK_FLAT_APPLICATION,
|
||||
morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1',
|
||||
createFieldInput: {
|
||||
name: 'targetPet',
|
||||
|
||||
+13
-3
@@ -4,17 +4,19 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
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';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { extractJunctionTargetSettingsFromSettings } from 'src/engine/metadata-modules/flat-field-metadata/utils/extract-junction-target-settings-from-settings.util';
|
||||
import { generateMorphOrRelationFlatFieldMetadataPair } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
|
||||
|
||||
type ComputeFlatFieldToUpdateFromMorphRelationUpdatePayloadArgs = {
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
morphRelationsUpdatePayload?: RelationUpdatePayload[];
|
||||
fieldMetadataToUpdate: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
|
||||
@@ -22,7 +24,7 @@ type ComputeFlatFieldToUpdateFromMorphRelationUpdatePayloadArgs = {
|
||||
};
|
||||
|
||||
export const computeFlatFieldToUpdateFromMorphRelationUpdatePayload = ({
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
flatFieldMetadataMaps,
|
||||
morphRelationsUpdatePayload,
|
||||
fieldMetadataToUpdate,
|
||||
@@ -68,6 +70,13 @@ export const computeFlatFieldToUpdateFromMorphRelationUpdatePayload = ({
|
||||
const commonTargetFieldName = initialTargetFieldMetadata.name;
|
||||
const commonObjectMetadataId = initialTargetFieldMetadata.objectMetadataId;
|
||||
|
||||
const { junctionTargetFieldId } = extractJunctionTargetSettingsFromSettings(
|
||||
fieldMetadataToUpdate.settings,
|
||||
);
|
||||
const junctionTargetFlatFieldMetadata = isDefined(junctionTargetFieldId)
|
||||
? flatFieldMetadataMaps.byId[junctionTargetFieldId]
|
||||
: undefined;
|
||||
|
||||
morphRelationsUpdatePayload.forEach((morphRelationUpdatePayload) => {
|
||||
const { targetObjectMetadataId } = morphRelationUpdatePayload;
|
||||
|
||||
@@ -101,13 +110,14 @@ export const computeFlatFieldToUpdateFromMorphRelationUpdatePayload = ({
|
||||
targetFlatObjectMetadata: newTargetObjectMetadata,
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
workspaceId: fieldMetadataToUpdate.workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
sourceFlatObjectMetadataJoinColumnName:
|
||||
computeMorphOrRelationFieldJoinColumnName({
|
||||
name: computedMorphName,
|
||||
}),
|
||||
morphId: fieldMetadataToUpdate.morphId,
|
||||
targetFieldName: commonTargetFieldName,
|
||||
junctionTargetFlatFieldMetadata,
|
||||
});
|
||||
|
||||
flatFieldMetadatasToCreate.push(...flatFieldMetadatas);
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ type JunctionTargetSettings = {
|
||||
junctionTargetFieldId?: string;
|
||||
};
|
||||
|
||||
// TODO refactor using either type predicate or FieldMetadataType discriminating union
|
||||
// Extracts junction target settings from untyped settings input
|
||||
// This function handles the boundary where settings come from external API input
|
||||
export const extractJunctionTargetSettingsFromSettings = (
|
||||
|
||||
+12
-6
@@ -10,6 +10,7 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { generateRatingOptions } from 'src/engine/metadata-modules/field-metadata/utils/generate-rating-optionts.util';
|
||||
@@ -25,14 +26,15 @@ import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-m
|
||||
export type FromCreateFieldInputToFlatObjectMetadataArgs = {
|
||||
createFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps'>;
|
||||
flatApplication: FlatApplication;
|
||||
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps' | 'flatFieldMetadataMaps'>;
|
||||
|
||||
export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
createFieldInput: rawCreateFieldInput,
|
||||
workspaceId,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatApplication,
|
||||
}: FromCreateFieldInputToFlatObjectMetadataArgs): Promise<
|
||||
FieldInputTranspilationResult<{
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
@@ -77,7 +79,9 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
fieldMetadataId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
objectMetadataUniversalIdentifier:
|
||||
parentFlatObjectMetadata.universalIdentifier,
|
||||
});
|
||||
|
||||
switch (createFieldInput.type) {
|
||||
@@ -88,21 +92,23 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
type: createFieldInput.type,
|
||||
},
|
||||
existingFlatObjectMetadataMaps,
|
||||
existingFlatFieldMetadataMaps,
|
||||
sourceFlatObjectMetadata: parentFlatObjectMetadata,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
});
|
||||
}
|
||||
case FieldMetadataType.RELATION: {
|
||||
return await fromRelationCreateFieldInputToFlatFieldMetadatas({
|
||||
existingFlatObjectMetadataMaps,
|
||||
existingFlatFieldMetadataMaps,
|
||||
sourceFlatObjectMetadata: parentFlatObjectMetadata,
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
type: createFieldInput.type,
|
||||
},
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
});
|
||||
}
|
||||
case FieldMetadataType.RATING: {
|
||||
|
||||
+19
-25
@@ -106,33 +106,27 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = ({
|
||||
[],
|
||||
viewFieldIds: fieldMetadataEntity.viewFields.map(({ id }) => id),
|
||||
viewFilterIds: fieldMetadataEntity.viewFilters.map(({ id }) => id),
|
||||
__universal: {
|
||||
universalIdentifier: fieldMetadataWithoutRelations.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier,
|
||||
viewFieldUniversalIdentifiers: fieldMetadataEntity.viewFields.map(
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier,
|
||||
viewFieldUniversalIdentifiers: fieldMetadataEntity.viewFields.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
viewFilterUniversalIdentifiers: fieldMetadataEntity.viewFilters.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
kanbanAggregateOperationViewUniversalIdentifiers:
|
||||
fieldMetadataEntity.kanbanAggregateOperationViews.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
viewFilterUniversalIdentifiers: fieldMetadataEntity.viewFilters.map(
|
||||
calendarViewUniversalIdentifiers: fieldMetadataEntity.calendarViews.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers:
|
||||
fieldMetadataEntity.mainGroupByFieldMetadataViews?.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
kanbanAggregateOperationViewUniversalIdentifiers:
|
||||
fieldMetadataEntity.kanbanAggregateOperationViews.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
calendarViewUniversalIdentifiers: fieldMetadataEntity.calendarViews.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers:
|
||||
fieldMetadataEntity.mainGroupByFieldMetadataViews?.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
) ?? [],
|
||||
settings: settingsWithUniversalIdentifiers,
|
||||
defaultValue: fieldMetadataWithoutRelations.defaultValue,
|
||||
options: fieldMetadataWithoutRelations.options,
|
||||
standardOverrides: fieldMetadataWithoutRelations.standardOverrides,
|
||||
},
|
||||
) ?? [],
|
||||
universalSettings: settingsWithUniversalIdentifiers,
|
||||
};
|
||||
};
|
||||
|
||||
+15
-3
@@ -3,12 +3,14 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
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';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { extractJunctionTargetSettingsFromSettings } from 'src/engine/metadata-modules/flat-field-metadata/utils/extract-junction-target-settings-from-settings.util';
|
||||
import { generateMorphOrRelationFlatFieldMetadataPair } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util';
|
||||
import { validateMorphRelationCreationPayload } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-morph-relation-creation-payload.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -19,16 +21,18 @@ type FromMorphRelationCreateFieldInputToFlatFieldMetadatasArgs = {
|
||||
type: FieldMetadataType.MORPH_RELATION;
|
||||
};
|
||||
existingFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
existingFlatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
sourceFlatObjectMetadata: FlatObjectMetadata;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
createFieldInput,
|
||||
existingFlatObjectMetadataMaps,
|
||||
existingFlatFieldMetadataMaps,
|
||||
sourceFlatObjectMetadata,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: FromMorphRelationCreateFieldInputToFlatFieldMetadatasArgs): Promise<
|
||||
FieldInputTranspilationResult<{
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
@@ -66,6 +70,13 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
return morphRelationCreationPayloadValidation;
|
||||
}
|
||||
|
||||
const { junctionTargetFieldId } = extractJunctionTargetSettingsFromSettings(
|
||||
createFieldInput.settings,
|
||||
);
|
||||
const junctionTargetFlatFieldMetadata = isDefined(junctionTargetFieldId)
|
||||
? existingFlatFieldMetadataMaps.byId[junctionTargetFieldId]
|
||||
: undefined;
|
||||
|
||||
const morphRelationCreationPayload =
|
||||
morphRelationCreationPayloadValidation.result;
|
||||
const morphId = v4();
|
||||
@@ -95,7 +106,8 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
workspaceId,
|
||||
morphId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
junctionTargetFlatFieldMetadata,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+16
-3
@@ -2,11 +2,14 @@ import { msg } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
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';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { extractJunctionTargetSettingsFromSettings } from 'src/engine/metadata-modules/flat-field-metadata/utils/extract-junction-target-settings-from-settings.util';
|
||||
import {
|
||||
generateMorphOrRelationFlatFieldMetadataPair,
|
||||
type SourceTargetMorphOrRelationFlatFieldAndFlatIndex,
|
||||
@@ -19,16 +22,18 @@ type FromRelationCreateFieldInputToFlatFieldMetadataArgs = {
|
||||
type: FieldMetadataType.RELATION;
|
||||
};
|
||||
existingFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
existingFlatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
sourceFlatObjectMetadata: FlatObjectMetadata;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
existingFlatFieldMetadataMaps,
|
||||
sourceFlatObjectMetadata,
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
|
||||
FieldInputTranspilationResult<SourceTargetMorphOrRelationFlatFieldAndFlatIndex>
|
||||
> => {
|
||||
@@ -59,6 +64,13 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
const { relationCreationPayload, targetFlatObjectMetadata } =
|
||||
relationValidationResult.result;
|
||||
|
||||
const { junctionTargetFieldId } = extractJunctionTargetSettingsFromSettings(
|
||||
createFieldInput.settings,
|
||||
);
|
||||
const junctionTargetFlatFieldMetadata = isDefined(junctionTargetFieldId)
|
||||
? existingFlatFieldMetadataMaps.byId[junctionTargetFieldId]
|
||||
: undefined;
|
||||
|
||||
const generateResult = generateMorphOrRelationFlatFieldMetadataPair({
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
@@ -72,7 +84,8 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
targetFlatObjectMetadata,
|
||||
targetFlatFieldMetadataType: FieldMetadataType.RELATION,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
junctionTargetFlatFieldMetadata,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+5
-4
@@ -5,6 +5,7 @@ import {
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
@@ -23,7 +24,7 @@ import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-fiel
|
||||
|
||||
type FromUpdateFieldInputToFlatFieldMetadataArgs = {
|
||||
updateFieldInput: UpdateFieldInput;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
isSystemBuild: boolean;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
@@ -41,7 +42,7 @@ type FlatFieldMetadataAndIndexToUpdate = {
|
||||
flatFieldMetadatasToCreate: FlatFieldMetadata[];
|
||||
} & FlatFieldMetadataUpdateSideEffects;
|
||||
export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
flatIndexMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
@@ -109,7 +110,7 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
? computeFlatFieldToUpdateFromMorphRelationUpdatePayload({
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
morphRelationsUpdatePayload:
|
||||
rawUpdateFieldInput?.morphRelationsUpdatePayload,
|
||||
flatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
@@ -149,7 +150,7 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
toFlatFieldMetadata,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
});
|
||||
|
||||
if (sideEffectResult.status === 'fail') {
|
||||
|
||||
+73
-31
@@ -6,11 +6,11 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { extractJunctionTargetSettingsFromSettings } from 'src/engine/metadata-modules/flat-field-metadata/utils/extract-junction-target-settings-from-settings.util';
|
||||
import { generateIndexForFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-index-for-flat-field-metadata.util';
|
||||
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -22,23 +22,39 @@ type ComputeFieldMetadataRelationSettingsForRelationTypeArgs = {
|
||||
relationType: RelationType;
|
||||
joinColumnName: string;
|
||||
junctionTargetFieldId?: string;
|
||||
junctionTargetFieldUniversalIdentifier?: string;
|
||||
};
|
||||
|
||||
const computeFieldMetadataRelationSettingsForRelationType = ({
|
||||
relationType,
|
||||
joinColumnName,
|
||||
junctionTargetFieldId,
|
||||
}: ComputeFieldMetadataRelationSettingsForRelationTypeArgs) => {
|
||||
junctionTargetFieldUniversalIdentifier,
|
||||
}: ComputeFieldMetadataRelationSettingsForRelationTypeArgs): Pick<
|
||||
FlatFieldMetadata<MorphOrRelationFieldMetadataType>,
|
||||
'settings' | 'universalSettings'
|
||||
> => {
|
||||
if (relationType === RelationType.MANY_TO_ONE) {
|
||||
return {
|
||||
const settings = {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName,
|
||||
};
|
||||
|
||||
return { settings, universalSettings: settings };
|
||||
}
|
||||
|
||||
return {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
...(junctionTargetFieldId && { junctionTargetFieldId }),
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
...(junctionTargetFieldId && { junctionTargetFieldId }),
|
||||
},
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
...(junctionTargetFieldUniversalIdentifier && {
|
||||
junctionTargetFieldUniversalIdentifier,
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -53,8 +69,9 @@ type GenerateMorphOrRelationFlatFieldMetadataPairArgs = {
|
||||
> & { type: MorphOrRelationFieldMetadataType };
|
||||
workspaceId: string;
|
||||
morphId?: string | null;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
targetFieldName?: string;
|
||||
junctionTargetFlatFieldMetadata?: FlatFieldMetadata;
|
||||
};
|
||||
|
||||
export type SourceTargetMorphOrRelationFlatFieldAndFlatIndex = {
|
||||
@@ -68,27 +85,30 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
targetFlatObjectMetadata,
|
||||
targetFlatFieldMetadataType,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
sourceFlatObjectMetadataJoinColumnName,
|
||||
morphId = null,
|
||||
targetFieldName,
|
||||
junctionTargetFlatFieldMetadata,
|
||||
}: GenerateMorphOrRelationFlatFieldMetadataPairArgs): SourceTargetMorphOrRelationFlatFieldAndFlatIndex => {
|
||||
const sourceFlatFieldMetadataType = createFieldInput.type;
|
||||
|
||||
const { relationCreationPayload } = createFieldInput;
|
||||
|
||||
const { junctionTargetFieldId } = extractJunctionTargetSettingsFromSettings(
|
||||
createFieldInput.settings,
|
||||
);
|
||||
|
||||
const sourceFlatFieldMetadataSettings =
|
||||
computeFieldMetadataRelationSettingsForRelationType({
|
||||
joinColumnName: sourceFlatObjectMetadataJoinColumnName,
|
||||
relationType: relationCreationPayload.type,
|
||||
junctionTargetFieldId,
|
||||
});
|
||||
const {
|
||||
settings: sourceFlatFieldMetadataSettings,
|
||||
universalSettings: sourceFlatFieldMetadataUniversalSettings,
|
||||
} = computeFieldMetadataRelationSettingsForRelationType({
|
||||
joinColumnName: sourceFlatObjectMetadataJoinColumnName,
|
||||
relationType: relationCreationPayload.type,
|
||||
junctionTargetFieldId: junctionTargetFlatFieldMetadata?.id,
|
||||
junctionTargetFieldUniversalIdentifier:
|
||||
junctionTargetFlatFieldMetadata?.universalIdentifier,
|
||||
});
|
||||
const targetRelationTargetFieldMetadataId = v4();
|
||||
const sourceRelationTargetFieldMetadataId = v4();
|
||||
const sourceFieldUniversalIdentifier = v4();
|
||||
const targetFieldUniversalIdentifier = v4();
|
||||
|
||||
const defaultDescriptionFromField =
|
||||
buildDescriptionForRelationFieldMetadataOnFromField({
|
||||
@@ -105,10 +125,15 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
'flatRelationTargetFieldMetadata'
|
||||
> = {
|
||||
...getDefaultFlatFieldMetadata({
|
||||
createFieldInput,
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
universalIdentifier: sourceFieldUniversalIdentifier,
|
||||
},
|
||||
workspaceId,
|
||||
fieldMetadataId: sourceRelationTargetFieldMetadataId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
objectMetadataUniversalIdentifier:
|
||||
sourceFlatObjectMetadata.universalIdentifier,
|
||||
}),
|
||||
morphId:
|
||||
sourceFlatFieldMetadataType === FieldMetadataType.MORPH_RELATION
|
||||
@@ -121,9 +146,14 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
createFieldInput.description ?? defaultDescriptionFromField.description,
|
||||
defaultValue: null,
|
||||
settings: sourceFlatFieldMetadataSettings,
|
||||
universalSettings: sourceFlatFieldMetadataUniversalSettings,
|
||||
options: null,
|
||||
relationTargetFieldMetadataId: targetRelationTargetFieldMetadataId,
|
||||
relationTargetObjectMetadataId: targetFlatObjectMetadata.id,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
targetFlatObjectMetadata.universalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
targetFieldUniversalIdentifier,
|
||||
};
|
||||
|
||||
const targetCreateFieldInput: CreateFieldInput = {
|
||||
@@ -141,25 +171,32 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
workspaceId,
|
||||
isSystem: createFieldInput.isSystem ?? false,
|
||||
};
|
||||
const targetFlatFieldMetadataSettings =
|
||||
computeFieldMetadataRelationSettingsForRelationType({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: targetCreateFieldInput.name,
|
||||
}),
|
||||
relationType:
|
||||
relationCreationPayload.type === RelationType.ONE_TO_MANY
|
||||
? RelationType.MANY_TO_ONE
|
||||
: RelationType.ONE_TO_MANY,
|
||||
});
|
||||
const {
|
||||
settings: targetFlatFieldMetadataSettings,
|
||||
universalSettings: targetFlatFieldMetadataUniversalSettings,
|
||||
} = computeFieldMetadataRelationSettingsForRelationType({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: targetCreateFieldInput.name,
|
||||
}),
|
||||
relationType:
|
||||
relationCreationPayload.type === RelationType.ONE_TO_MANY
|
||||
? RelationType.MANY_TO_ONE
|
||||
: RelationType.ONE_TO_MANY,
|
||||
});
|
||||
|
||||
const targetFlatFieldMetadata: FlatFieldMetadata<
|
||||
typeof targetFlatFieldMetadataType
|
||||
> = {
|
||||
...getDefaultFlatFieldMetadata({
|
||||
createFieldInput: targetCreateFieldInput,
|
||||
createFieldInput: {
|
||||
...targetCreateFieldInput,
|
||||
universalIdentifier: targetFieldUniversalIdentifier,
|
||||
},
|
||||
workspaceId,
|
||||
fieldMetadataId: targetRelationTargetFieldMetadataId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
objectMetadataUniversalIdentifier:
|
||||
targetFlatObjectMetadata.universalIdentifier,
|
||||
}),
|
||||
morphId:
|
||||
targetFlatFieldMetadataType === FieldMetadataType.MORPH_RELATION
|
||||
@@ -168,9 +205,14 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
type: targetFlatFieldMetadataType,
|
||||
defaultValue: null,
|
||||
settings: targetFlatFieldMetadataSettings,
|
||||
universalSettings: targetFlatFieldMetadataUniversalSettings,
|
||||
options: null,
|
||||
relationTargetFieldMetadataId: sourceRelationTargetFieldMetadataId,
|
||||
relationTargetObjectMetadataId: sourceFlatObjectMetadata.id,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
sourceFlatObjectMetadata.universalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
sourceFieldUniversalIdentifier,
|
||||
};
|
||||
|
||||
const indexMetadata: FlatIndexMetadata = generateIndexForFlatFieldMetadata({
|
||||
|
||||
+16
-4
@@ -1,6 +1,7 @@
|
||||
import { extractAndSanitizeObjectStringFields } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value';
|
||||
import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable';
|
||||
@@ -10,13 +11,15 @@ type GetDefaultFlatFieldMetadataArgs = {
|
||||
fieldMetadataId: string;
|
||||
createFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
objectMetadataUniversalIdentifier: string;
|
||||
};
|
||||
export const getDefaultFlatFieldMetadata = ({
|
||||
createFieldInput,
|
||||
fieldMetadataId,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
objectMetadataUniversalIdentifier,
|
||||
}: GetDefaultFlatFieldMetadataArgs) => {
|
||||
const { defaultValue, settings } = extractAndSanitizeObjectStringFields(
|
||||
createFieldInput,
|
||||
@@ -58,9 +61,18 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
updatedAt: createdAt,
|
||||
isUIReadOnly: createFieldInput.isUIReadOnly ?? false,
|
||||
morphId: null,
|
||||
applicationId:
|
||||
createFieldInput.applicationId ?? workspaceCustomApplicationId,
|
||||
applicationId: flatApplication.id,
|
||||
viewFilterIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
} as const satisfies FlatFieldMetadata;
|
||||
};
|
||||
|
||||
+4
-3
@@ -1,6 +1,7 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
@@ -41,7 +42,7 @@ type HandleFlatFieldMetadataUpdateSideEffectArgs = FromTo<
|
||||
| 'flatViewMaps'
|
||||
| 'flatViewFieldMaps'
|
||||
> & {
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
|
||||
export const FLAT_FIELD_METADATA_UPDATE_EMPTY_SIDE_EFFECTS: FlatFieldMetadataUpdateSideEffects =
|
||||
@@ -70,7 +71,7 @@ export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
flatViewGroupMaps,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: HandleFlatFieldMetadataUpdateSideEffectArgs): FieldInputTranspilationResult<FlatFieldMetadataUpdateSideEffects> => {
|
||||
const sideEffectResult = structuredClone(
|
||||
FLAT_FIELD_METADATA_UPDATE_EMPTY_SIDE_EFFECTS,
|
||||
@@ -130,7 +131,7 @@ export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
flatIndexMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
});
|
||||
|
||||
if (indexChangesSideEffectResult.status === 'fail') {
|
||||
|
||||
+7
-6
@@ -2,6 +2,7 @@ import { msg } from '@lingui/core/macro';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
@@ -28,7 +29,7 @@ type FromToFlatFieldMetadataAndFlatEntityMaps = FromTo<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps' | 'flatFieldMetadataMaps' | 'flatIndexMaps'
|
||||
> & {
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
};
|
||||
const FIELD_METADATA_UPDATE_INDEX_SIDE_EFFECT: FieldMetadataUpdateIndexSideEffect =
|
||||
{
|
||||
@@ -43,7 +44,7 @@ export const handleIndexChangesDuringFieldUpdate = ({
|
||||
flatIndexMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: FromToFlatFieldMetadataAndFlatEntityMaps): FieldInputTranspilationResult<FieldMetadataUpdateIndexSideEffect> => {
|
||||
if (
|
||||
!hasIndexRelevantChanges({
|
||||
@@ -81,7 +82,7 @@ export const handleIndexChangesDuringFieldUpdate = ({
|
||||
relatedIndexes,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -127,13 +128,13 @@ const handleExistingIndexes = ({
|
||||
relatedIndexes,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
}: {
|
||||
relatedIndexes: FlatIndexMetadata[];
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: AllFlatEntityMaps['flatFieldMetadataMaps'];
|
||||
} & FromTo<FlatFieldMetadata, 'flatFieldMetadata'> & {
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
}): FieldInputTranspilationResult<FieldMetadataUpdateIndexSideEffect> => {
|
||||
if (
|
||||
toFlatFieldMetadata.isUnique === false &&
|
||||
@@ -155,7 +156,7 @@ const handleExistingIndexes = ({
|
||||
if (
|
||||
isDefined(uniqueIndexToDelete) &&
|
||||
((isDefined(uniqueIndexToDelete.applicationId) &&
|
||||
uniqueIndexToDelete.applicationId !== workspaceCustomApplicationId) ||
|
||||
uniqueIndexToDelete.applicationId !== flatApplication.id) ||
|
||||
!uniqueIndexToDelete.isCustom)
|
||||
) {
|
||||
return {
|
||||
|
||||
+16
-3
@@ -10,6 +10,11 @@ export const getFlatObjectMetadataMock = (
|
||||
overrides: FlatObjectMetadataOverrides,
|
||||
): FlatObjectMetadata => {
|
||||
const createdAt = '2024-01-01T00:00:00.000Z';
|
||||
const applicationId = overrides.applicationId ?? faker.string.uuid();
|
||||
const labelIdentifierFieldMetadataId =
|
||||
overrides.labelIdentifierFieldMetadataId ?? faker.string.uuid();
|
||||
const imageIdentifierFieldMetadataId =
|
||||
overrides.imageIdentifierFieldMetadataId ?? faker.string.uuid();
|
||||
|
||||
return {
|
||||
viewIds: [],
|
||||
@@ -18,7 +23,7 @@ export const getFlatObjectMetadataMock = (
|
||||
description: 'default flat object metadata description',
|
||||
icon: 'icon',
|
||||
id: faker.string.uuid(),
|
||||
imageIdentifierFieldMetadataId: faker.string.uuid(),
|
||||
imageIdentifierFieldMetadataId,
|
||||
isActive: true,
|
||||
isAuditLogged: true,
|
||||
isCustom: true,
|
||||
@@ -27,20 +32,28 @@ export const getFlatObjectMetadataMock = (
|
||||
isSearchable: true,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
labelIdentifierFieldMetadataId: faker.string.uuid(),
|
||||
labelIdentifierFieldMetadataId,
|
||||
labelPlural: 'default flat object metadata label plural',
|
||||
labelSingular: 'default flat object metadata label singular',
|
||||
namePlural: 'defaultflatObjectMetadataNamePlural',
|
||||
nameSingular: 'defaultflatObjectMetadataNameSingular',
|
||||
shortcut: 'shortcut',
|
||||
standardId: null,
|
||||
applicationId: faker.string.uuid(),
|
||||
applicationId,
|
||||
standardOverrides: null,
|
||||
targetTableName: '',
|
||||
workspaceId: faker.string.uuid(),
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
duplicateCriteria: null,
|
||||
applicationUniversalIdentifier: applicationId,
|
||||
fieldUniversalIdentifiers: [],
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
labelIdentifierFieldMetadataId,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier:
|
||||
imageIdentifierFieldMetadataId,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
+7
-8
@@ -1,13 +1,12 @@
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type FlatEntityFromV2 } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from-v2.type';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
type BaseFlatObjectMetadata = FlatEntityFrom<
|
||||
type BaseFlatObjectMetadata = FlatEntityFromV2<
|
||||
Omit<ObjectMetadataEntity, 'targetRelationFields' | 'dataSourceId'>
|
||||
>;
|
||||
export type FlatObjectMetadata = Omit<BaseFlatObjectMetadata, '__universal'> & {
|
||||
__universal?: BaseFlatObjectMetadata['__universal'] & {
|
||||
// TODO remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: string | null;
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: string | null;
|
||||
};
|
||||
export type FlatObjectMetadata = BaseFlatObjectMetadata & {
|
||||
// NOTE: below fields are not reflected on the final UniversalFlatEntity either they should we should define a common source
|
||||
// TODO remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: string | null;
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: string | null;
|
||||
};
|
||||
|
||||
+20
-9
@@ -6,6 +6,7 @@ import { v4 } from 'uuid';
|
||||
|
||||
import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -19,14 +20,14 @@ type FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs
|
||||
{
|
||||
createObjectInput: CreateObjectInput;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
flatApplication: FlatApplication;
|
||||
existingFeatureFlagsMap: FeatureFlagMap;
|
||||
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps'>;
|
||||
export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate =
|
||||
({
|
||||
createObjectInput: rawCreateObjectInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
existingFeatureFlagsMap,
|
||||
}: FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs): {
|
||||
@@ -50,11 +51,14 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
);
|
||||
|
||||
const objectMetadataId = v4();
|
||||
const universalIdentifier = createObjectInput.universalIdentifier ?? v4();
|
||||
const defaultFlatFieldForCustomObjectMaps =
|
||||
buildDefaultFlatFieldMetadatasForCustomObject({
|
||||
flatObjectMetadata: {
|
||||
id: objectMetadataId,
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
applicationId: flatApplication.id,
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
universalIdentifier,
|
||||
},
|
||||
workspaceId,
|
||||
skipNameField: createObjectInput.skipNameField,
|
||||
@@ -62,11 +66,12 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
const createdAt = new Date().toISOString();
|
||||
|
||||
// Use nameField.id if it exists, otherwise use idField.id (for junction tables without name)
|
||||
const nameField = defaultFlatFieldForCustomObjectMaps.fields.nameField as
|
||||
| FlatFieldMetadata
|
||||
| undefined;
|
||||
const nameField = defaultFlatFieldForCustomObjectMaps.fields.nameField;
|
||||
const labelIdentifierFieldMetadataId =
|
||||
nameField?.id ?? defaultFlatFieldForCustomObjectMaps.fields.idField.id;
|
||||
const labelIdentifierFieldMetadataUniversalIdentifier =
|
||||
nameField?.universalIdentifier ??
|
||||
defaultFlatFieldForCustomObjectMaps.fields.idField.universalIdentifier;
|
||||
|
||||
const flatObjectMetadataToCreate: FlatObjectMetadata = {
|
||||
fieldIds: [],
|
||||
@@ -95,10 +100,16 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
shortcut: createObjectInput.shortcut ?? null,
|
||||
standardId: createObjectInput.standardId ?? null,
|
||||
standardOverrides: null,
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
universalIdentifier: createObjectInput.universalIdentifier ?? v4(),
|
||||
applicationId: flatApplication.id,
|
||||
universalIdentifier,
|
||||
targetTableName: 'DEPRECATED',
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
fieldUniversalIdentifiers: [],
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -108,7 +119,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceFlatObjectMetadata: flatObjectMetadataToCreate,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatApplication,
|
||||
existingFeatureFlagsMap,
|
||||
});
|
||||
|
||||
|
||||
+12
-19
@@ -76,24 +76,17 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = ({
|
||||
viewIds: objectMetadataEntity.views.map(({ id }) => id),
|
||||
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(({ id }) => id),
|
||||
fieldIds: objectMetadataEntity.fields.map(({ id }) => id),
|
||||
__universal: {
|
||||
universalIdentifier:
|
||||
objectMetadataEntityWithoutRelations.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier,
|
||||
fieldUniversalIdentifiers: objectMetadataEntity.fields.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
indexMetadataUniversalIdentifiers:
|
||||
objectMetadataEntity.indexMetadatas.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
viewUniversalIdentifiers: objectMetadataEntity.views.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
standardOverrides: objectMetadataEntityWithoutRelations.standardOverrides,
|
||||
duplicateCriteria: objectMetadataEntityWithoutRelations.duplicateCriteria,
|
||||
},
|
||||
applicationUniversalIdentifier,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier,
|
||||
fieldUniversalIdentifiers: objectMetadataEntity.fields.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
indexMetadataUniversalIdentifiers: objectMetadataEntity.indexMetadatas.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
viewUniversalIdentifiers: objectMetadataEntity.views.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ type PageLayoutWidgetConfiguration = PageLayoutWidgetEntity['configuration'];
|
||||
|
||||
type UniversalPageLayoutWidgetConfiguration = NonNullable<
|
||||
FlatPageLayoutWidget['__universal']
|
||||
>['configuration'];
|
||||
>['universalConfiguration'];
|
||||
|
||||
// Field metadata IDs in widget configurations don't need to reference existing entities
|
||||
const getFieldMetadataUniversalIdentifier = ({
|
||||
|
||||
+1
-4
@@ -85,10 +85,7 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
|
||||
applicationUniversalIdentifier,
|
||||
pageLayoutTabUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
gridPosition: pageLayoutWidgetEntityWithoutRelations.gridPosition,
|
||||
configuration: configurationWithUniversalIdentifiers,
|
||||
conditionalDisplay:
|
||||
pageLayoutWidgetEntityWithoutRelations.conditionalDisplay,
|
||||
universalConfiguration: configurationWithUniversalIdentifiers,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
-1
@@ -129,7 +129,6 @@ export const fromRowLevelPermissionPredicateEntityToFlatRowLevelPermissionPredic
|
||||
roleUniversalIdentifier,
|
||||
workspaceMemberFieldMetadataUniversalIdentifier,
|
||||
rowLevelPermissionPredicateGroupUniversalIdentifier,
|
||||
value: rowLevelPermissionPredicateEntityWithoutRelations.value,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
-1
@@ -81,7 +81,6 @@ export const fromViewFilterEntityToFlatViewFilter = ({
|
||||
fieldMetadataUniversalIdentifier,
|
||||
viewFilterGroupUniversalIdentifier,
|
||||
viewUniversalIdentifier,
|
||||
value: viewFilterEntityWithoutRelations.value,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
-6
@@ -37,12 +37,6 @@ export const fromLogicFunctionEntityToFlatLogicFunction = ({
|
||||
__universal: {
|
||||
universalIdentifier: logicFunctionEntity.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
publishedVersions: logicFunctionEntity.publishedVersions,
|
||||
toolInputSchema: logicFunctionEntity.toolInputSchema,
|
||||
cronTriggerSettings: logicFunctionEntity.cronTriggerSettings,
|
||||
databaseEventTriggerSettings:
|
||||
logicFunctionEntity.databaseEventTriggerSettings,
|
||||
httpRouteTriggerSettings: logicFunctionEntity.httpRouteTriggerSettings,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+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);
|
||||
|
||||
+16
@@ -123,6 +123,12 @@ describe('WorkspaceEntityManager', () => {
|
||||
duplicateCriteria: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: ['field-id'],
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
};
|
||||
|
||||
(getObjectMetadataFromEntityTarget as jest.Mock).mockReturnValue(
|
||||
@@ -162,6 +168,16 @@ describe('WorkspaceEntityManager', () => {
|
||||
relationTargetObjectMetadataId: null,
|
||||
morphId: null,
|
||||
applicationId: 'application-id',
|
||||
applicationUniversalIdentifier: 'application-id',
|
||||
objectMetadataUniversalIdentifier: 'test-entity-id',
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
const flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata> = {
|
||||
|
||||
+10
@@ -97,6 +97,16 @@ describe('WorkspaceRepository', () => {
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
viewFieldIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
applicationUniversalIdentifier: 'application-id',
|
||||
objectMetadataUniversalIdentifier: 'test-metadata-id',
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
|
||||
mockInternalContext = {
|
||||
|
||||
+38
-33
@@ -8,39 +8,44 @@ import { getColumnNameToFieldMetadataIdMap } from 'src/engine/twenty-orm/utils/g
|
||||
describe('getColumnNameToFieldMetadataIdMap', () => {
|
||||
const createMockFlatObjectMetadata = (
|
||||
fieldIds: string[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
}) as FlatObjectMetadata;
|
||||
): FlatObjectMetadata => ({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: fieldIds,
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
});
|
||||
|
||||
const createMockFlatFieldMetadata = (
|
||||
id: string,
|
||||
|
||||
+38
-33
@@ -8,39 +8,44 @@ import { getFieldMetadataIdToColumnNamesMap } from 'src/engine/twenty-orm/utils/
|
||||
describe('getFieldMetadataIdToColumnNamesMap', () => {
|
||||
const createMockFlatObjectMetadata = (
|
||||
fieldIds: string[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
}) as FlatObjectMetadata;
|
||||
): FlatObjectMetadata => ({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: fieldIds,
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
});
|
||||
|
||||
const createMockFlatFieldMetadata = (
|
||||
id: string,
|
||||
|
||||
+38
-33
@@ -8,39 +8,44 @@ import { isRecordMatchingRLSRowLevelPermissionPredicate } from 'src/engine/twent
|
||||
describe('isRecordMatchingRLSRowLevelPermissionPredicate', () => {
|
||||
const createMockFlatObjectMetadata = (
|
||||
fieldIds: string[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
}) as FlatObjectMetadata;
|
||||
): FlatObjectMetadata => ({
|
||||
id: 'test-object-id',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
labelSingular: 'Test',
|
||||
labelPlural: 'Tests',
|
||||
icon: 'IconTest',
|
||||
targetTableName: 'test',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: false,
|
||||
isSearchable: false,
|
||||
workspaceId: 'test-workspace-id',
|
||||
universalIdentifier: 'test-object-id',
|
||||
indexMetadataIds: [],
|
||||
fieldIds,
|
||||
viewIds: [],
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
standardId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
duplicateCriteria: null,
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
fieldUniversalIdentifiers: fieldIds,
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
});
|
||||
|
||||
const createMockFlatFieldMetadata = (
|
||||
id: string,
|
||||
|
||||
+11
@@ -100,5 +100,16 @@ export const createStandardFieldFlatMetadata = <
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
applicationUniversalIdentifier: twentyStandardApplicationId,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS[objectName].universalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
relationTargetFieldMetadataUniversalIdentifier: null,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
};
|
||||
|
||||
+17
@@ -83,6 +83,10 @@ export const createStandardRelationFieldFlatMetadata = <
|
||||
const targetFieldIds =
|
||||
standardObjectMetadataRelatedEntityIds[targetObjectName].fields;
|
||||
|
||||
const targetObjectFields = STANDARD_OBJECTS[targetObjectName].fields;
|
||||
const targetFieldDefinition =
|
||||
targetObjectFields[targetFieldName as keyof typeof targetObjectFields];
|
||||
|
||||
return {
|
||||
id: fieldIds[fieldName as keyof typeof fieldIds].id,
|
||||
universalIdentifier: fieldDefinition.universalIdentifier,
|
||||
@@ -117,5 +121,18 @@ export const createStandardRelationFieldFlatMetadata = <
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
applicationUniversalIdentifier: twentyStandardApplicationId,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS[objectName].universalIdentifier,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS[targetObjectName].universalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
targetFieldDefinition.universalIdentifier,
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
kanbanAggregateOperationViewUniversalIdentifiers: [],
|
||||
calendarViewUniversalIdentifiers: [],
|
||||
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
|
||||
universalSettings: null,
|
||||
};
|
||||
};
|
||||
|
||||
+53
-38
@@ -1,4 +1,5 @@
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant';
|
||||
import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type';
|
||||
@@ -52,42 +53,56 @@ export const createStandardObjectFlatMetadata = <
|
||||
standardObjectMetadataRelatedEntityIds,
|
||||
twentyStandardApplicationId,
|
||||
now,
|
||||
}: CreateStandardObjectArgs<O>): FlatObjectMetadata => ({
|
||||
universalIdentifier,
|
||||
standardId: universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
workspaceId,
|
||||
nameSingular,
|
||||
namePlural,
|
||||
labelSingular,
|
||||
labelPlural,
|
||||
description,
|
||||
icon,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem,
|
||||
isSearchable,
|
||||
isAuditLogged,
|
||||
isUIReadOnly,
|
||||
isLabelSyncedWithName: false,
|
||||
standardOverrides: null,
|
||||
duplicateCriteria,
|
||||
shortcut,
|
||||
labelIdentifierFieldMetadataId:
|
||||
standardObjectMetadataRelatedEntityIds[nameSingular].fields[
|
||||
}: CreateStandardObjectArgs<O>): FlatObjectMetadata => {
|
||||
const labelIdentifierFieldMetadataUniversalIdentifier =
|
||||
// @ts-expect-error ignore
|
||||
STANDARD_OBJECTS[nameSingular as keyof typeof STANDARD_OBJECTS].fields[
|
||||
labelIdentifierFieldMetadataName
|
||||
].id,
|
||||
imageIdentifierFieldMetadataId: imageIdentifierFieldMetadataName
|
||||
? standardObjectMetadataRelatedEntityIds[nameSingular].fields[
|
||||
imageIdentifierFieldMetadataName
|
||||
].id
|
||||
: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
fieldIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
id: standardObjectMetadataRelatedEntityIds[nameSingular].id,
|
||||
});
|
||||
].universalIdentifier;
|
||||
|
||||
return {
|
||||
universalIdentifier,
|
||||
standardId: universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
workspaceId,
|
||||
nameSingular,
|
||||
namePlural,
|
||||
labelSingular,
|
||||
labelPlural,
|
||||
description,
|
||||
icon,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem,
|
||||
isSearchable,
|
||||
isAuditLogged,
|
||||
isUIReadOnly,
|
||||
isLabelSyncedWithName: false,
|
||||
standardOverrides: null,
|
||||
duplicateCriteria,
|
||||
shortcut,
|
||||
labelIdentifierFieldMetadataId:
|
||||
standardObjectMetadataRelatedEntityIds[nameSingular].fields[
|
||||
labelIdentifierFieldMetadataName
|
||||
].id,
|
||||
imageIdentifierFieldMetadataId: imageIdentifierFieldMetadataName
|
||||
? standardObjectMetadataRelatedEntityIds[nameSingular].fields[
|
||||
imageIdentifierFieldMetadataName
|
||||
].id
|
||||
: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
fieldIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
id: standardObjectMetadataRelatedEntityIds[nameSingular].id,
|
||||
applicationUniversalIdentifier: twentyStandardApplicationId,
|
||||
fieldUniversalIdentifiers: [],
|
||||
viewUniversalIdentifiers: [],
|
||||
indexMetadataUniversalIdentifiers: [],
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
};
|
||||
};
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
|
||||
import { type ExtractJsonbProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/extract-jsonb-properties.type';
|
||||
|
||||
export const ALL_JSONB_PROPERTIES_BY_METADATA_NAME = {
|
||||
fieldMetadata: {
|
||||
defaultValue: 'defaultValue',
|
||||
standardOverrides: 'standardOverrides',
|
||||
options: 'options',
|
||||
settings: 'settings',
|
||||
},
|
||||
objectMetadata: {
|
||||
standardOverrides: 'standardOverrides',
|
||||
duplicateCriteria: 'duplicateCriteria',
|
||||
},
|
||||
view: {},
|
||||
viewField: {},
|
||||
viewGroup: {},
|
||||
viewFilter: { value: 'value' },
|
||||
viewFilterGroup: {},
|
||||
index: {},
|
||||
role: {},
|
||||
roleTarget: {},
|
||||
rowLevelPermissionPredicate: { value: 'value' },
|
||||
rowLevelPermissionPredicateGroup: {},
|
||||
logicFunction: {
|
||||
cronTriggerSettings: 'cronTriggerSettings',
|
||||
databaseEventTriggerSettings: 'databaseEventTriggerSettings',
|
||||
httpRouteTriggerSettings: 'httpRouteTriggerSettings',
|
||||
publishedVersions: 'publishedVersions',
|
||||
toolInputSchema: 'toolInputSchema',
|
||||
},
|
||||
webhook: {},
|
||||
agent: {
|
||||
responseFormat: 'responseFormat',
|
||||
modelConfiguration: 'modelConfiguration',
|
||||
},
|
||||
skill: {},
|
||||
pageLayout: {},
|
||||
pageLayoutTab: {},
|
||||
pageLayoutWidget: {
|
||||
gridPosition: 'gridPosition',
|
||||
configuration: 'configuration',
|
||||
conditionalDisplay: 'conditionalDisplay',
|
||||
},
|
||||
commandMenuItem: {},
|
||||
navigationMenuItem: {},
|
||||
frontComponent: {},
|
||||
} as const satisfies {
|
||||
[P in AllMetadataName]: {
|
||||
[K in ExtractJsonbProperties<MetadataEntity<P>>]: K;
|
||||
};
|
||||
};
|
||||
|
||||
export type AllJsonbPropertiesForMetadataName<T extends AllMetadataName> =
|
||||
keyof (typeof ALL_JSONB_PROPERTIES_BY_METADATA_NAME)[T];
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
|
||||
import { type ExtractJsonbProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/extract-jsonb-properties.type';
|
||||
|
||||
export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
|
||||
fieldMetadata: {
|
||||
settings: 'settings',
|
||||
},
|
||||
objectMetadata: {},
|
||||
view: {},
|
||||
viewField: {},
|
||||
viewGroup: {},
|
||||
viewFilter: {},
|
||||
viewFilterGroup: {},
|
||||
index: {},
|
||||
role: {},
|
||||
roleTarget: {},
|
||||
rowLevelPermissionPredicate: {},
|
||||
rowLevelPermissionPredicateGroup: {},
|
||||
logicFunction: {},
|
||||
webhook: {},
|
||||
agent: {},
|
||||
skill: {},
|
||||
pageLayout: {},
|
||||
pageLayoutTab: {},
|
||||
pageLayoutWidget: {
|
||||
configuration: 'configuration',
|
||||
},
|
||||
commandMenuItem: {},
|
||||
navigationMenuItem: {},
|
||||
frontComponent: {},
|
||||
} as const satisfies {
|
||||
[P in AllMetadataName]: Partial<{
|
||||
[K in ExtractJsonbProperties<MetadataEntity<P>>]: K;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type AllJsonbPropertiesWithSerializedPropertiesForMetadataName<
|
||||
T extends AllMetadataName,
|
||||
> =
|
||||
keyof (typeof ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME)[T];
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import { type Equal, type Expect } from 'twenty-shared/testing';
|
||||
import { type SerializedRelation } from 'twenty-shared/types';
|
||||
|
||||
import { type ContainsSerializedRelation } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/contains-serialized-relation.type';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
type EmptyObject = {};
|
||||
|
||||
// ContainsSerializedRelation checks for SerializedRelation in object properties
|
||||
// It recurses into nested objects and arrays, but stops at primitives
|
||||
|
||||
// Direct property tests
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type DirectPropertyAssertions = [
|
||||
// Direct SerializedRelation property
|
||||
Expect<
|
||||
Equal<ContainsSerializedRelation<{ targetId: SerializedRelation }>, true>
|
||||
>,
|
||||
|
||||
// Nullable SerializedRelation property
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{ targetId: SerializedRelation | null }>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
|
||||
// Optional SerializedRelation property
|
||||
Expect<
|
||||
Equal<ContainsSerializedRelation<{ targetId?: SerializedRelation }>, true>
|
||||
>,
|
||||
|
||||
// Array of SerializedRelation
|
||||
Expect<
|
||||
Equal<ContainsSerializedRelation<{ ids: SerializedRelation[] }>, true>
|
||||
>,
|
||||
|
||||
// Plain properties only - no SerializedRelation
|
||||
Expect<
|
||||
Equal<ContainsSerializedRelation<{ name: string; count: number }>, false>
|
||||
>,
|
||||
|
||||
// Empty object
|
||||
Expect<Equal<ContainsSerializedRelation<EmptyObject>, false>>,
|
||||
];
|
||||
|
||||
// Nested object tests - should recurse
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type NestedObjectAssertions = [
|
||||
// Nested object with SerializedRelation
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
nested: { targetId: SerializedRelation };
|
||||
}>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
|
||||
// Deeply nested SerializedRelation
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
level1: { level2: { level3: { id: SerializedRelation } } };
|
||||
}>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
|
||||
// Nested object without SerializedRelation
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
nested: { name: string; count: number };
|
||||
}>,
|
||||
false
|
||||
>
|
||||
>,
|
||||
];
|
||||
|
||||
// Nested array tests - should recurse
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type NestedArrayAssertions = [
|
||||
// Array of objects with SerializedRelation
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
items: { targetId: SerializedRelation }[];
|
||||
}>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
|
||||
// 2D array of SerializedRelation
|
||||
Expect<
|
||||
Equal<ContainsSerializedRelation<{ matrix: SerializedRelation[][] }>, true>
|
||||
>,
|
||||
|
||||
// Array of plain objects
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
items: { name: string }[];
|
||||
}>,
|
||||
false
|
||||
>
|
||||
>,
|
||||
|
||||
// 2D array of strings
|
||||
Expect<Equal<ContainsSerializedRelation<{ matrix: string[][] }>, false>>,
|
||||
];
|
||||
|
||||
// Primitive types - should return false (not objects)
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type PrimitiveAssertions = [
|
||||
Expect<Equal<ContainsSerializedRelation<string>, false>>,
|
||||
Expect<Equal<ContainsSerializedRelation<number>, false>>,
|
||||
Expect<Equal<ContainsSerializedRelation<boolean>, false>>,
|
||||
Expect<Equal<ContainsSerializedRelation<null>, false>>,
|
||||
Expect<Equal<ContainsSerializedRelation<undefined>, false>>,
|
||||
];
|
||||
|
||||
// Real-world tests
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type RealWorldAssertions = [
|
||||
// Settings with SerializedRelation
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
relationType?: string;
|
||||
junctionTargetFieldId?: SerializedRelation;
|
||||
}>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
|
||||
// Workflow config with nested SerializedRelation in array
|
||||
Expect<
|
||||
Equal<
|
||||
ContainsSerializedRelation<{
|
||||
steps: { assigneeId: SerializedRelation; action: string }[];
|
||||
}>,
|
||||
true
|
||||
>
|
||||
>,
|
||||
];
|
||||
+3
-35
@@ -4,10 +4,8 @@ import {
|
||||
type HasAllProperties,
|
||||
} from 'twenty-shared/testing';
|
||||
import {
|
||||
type FieldMetadataDefaultOption,
|
||||
type FieldMetadataType,
|
||||
type FieldNumberVariant,
|
||||
type LinkMetadata,
|
||||
type NullablePartial,
|
||||
type NumberDataType,
|
||||
type RelationOnDeleteAction,
|
||||
@@ -91,8 +89,9 @@ type UniversalFlatTransformationAssertions = [
|
||||
>,
|
||||
];
|
||||
|
||||
// JSONB properties are now prefixed with 'universal' in UniversalFlatFieldMetadata
|
||||
type NarrowedTestCase =
|
||||
UniversalFlatFieldMetadata<FieldMetadataType.RELATION>['settings'];
|
||||
UniversalFlatFieldMetadata<FieldMetadataType.RELATION>['universalSettings'];
|
||||
|
||||
type NarrowedExpectedResult = {
|
||||
relationType: RelationType;
|
||||
@@ -107,7 +106,7 @@ type NarrowedExpectedResult = {
|
||||
|
||||
type SettingsTestCase = UniversalFlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.NUMBER | FieldMetadataType.TEXT
|
||||
>['settings'];
|
||||
>['universalSettings'];
|
||||
|
||||
type SettingsExpectedResult =
|
||||
| {
|
||||
@@ -132,39 +131,8 @@ type SettingsExpectedResult =
|
||||
}
|
||||
| null;
|
||||
|
||||
type DefaultValueTestCase = UniversalFlatFieldMetadata<
|
||||
| FieldMetadataType.RELATION
|
||||
| FieldMetadataType.NUMBER
|
||||
| FieldMetadataType.TEXT
|
||||
| FieldMetadataType.LINKS
|
||||
| FieldMetadataType.CURRENCY
|
||||
>['defaultValue'];
|
||||
|
||||
type DefaultValueExpectedResult =
|
||||
| string
|
||||
| number
|
||||
| null
|
||||
| {
|
||||
amountMicros: string | null;
|
||||
currencyCode: string | null;
|
||||
__JsonbPropertyBrand__?: undefined;
|
||||
}
|
||||
| {
|
||||
primaryLinkLabel: string | null;
|
||||
primaryLinkUrl: string | null;
|
||||
secondaryLinks: LinkMetadata[] | null;
|
||||
__JsonbPropertyBrand__?: undefined;
|
||||
};
|
||||
|
||||
type OptionsTestCase =
|
||||
UniversalFlatFieldMetadata<FieldMetadataType.RATING>['options'];
|
||||
|
||||
type OptionsExpectedResult = FieldMetadataDefaultOption[];
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
type Assertions = [
|
||||
Expect<Equal<SettingsTestCase, SettingsExpectedResult>>,
|
||||
Expect<Equal<NarrowedTestCase, NarrowedExpectedResult>>,
|
||||
Expect<Equal<DefaultValueTestCase, DefaultValueExpectedResult>>,
|
||||
Expect<Equal<OptionsTestCase, OptionsExpectedResult>>,
|
||||
];
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
type IsEmptyObject,
|
||||
type IsNever,
|
||||
type IsSerializedRelation,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
type ContainsSerializedRelationInner<T> = T extends unknown
|
||||
? IsNever<T> extends true
|
||||
? false
|
||||
: unknown extends T
|
||||
? false
|
||||
: IsSerializedRelation<T> extends true
|
||||
? true
|
||||
: T extends readonly (infer U)[]
|
||||
? ContainsSerializedRelationInner<U>
|
||||
: T extends object
|
||||
? IsEmptyObject<T> extends true
|
||||
? false
|
||||
: ContainsSerializedRelationInner<T[keyof T]>
|
||||
: false
|
||||
: never;
|
||||
|
||||
export type ContainsSerializedRelation<T> =
|
||||
true extends ContainsSerializedRelationInner<T> ? true : false;
|
||||
+1
-9
@@ -1,12 +1,4 @@
|
||||
import { type JSONB_PROPERTY_BRAND } from './jsonb-property.type';
|
||||
|
||||
export type HasJsonbPropertyBrand<T> =
|
||||
typeof JSONB_PROPERTY_BRAND extends keyof T ? true : false;
|
||||
|
||||
// Distributive check: returns `true` if any member of a union has the brand
|
||||
type HasJsonbBrandInUnion<T> = T extends unknown
|
||||
? HasJsonbPropertyBrand<T>
|
||||
: never;
|
||||
import { type HasJsonbBrandInUnion } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/has-jsonb-brand-in-union.type';
|
||||
|
||||
export type ExtractJsonbProperties<T> = NonNullable<
|
||||
{
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { type HasJsonbPropertyBrand } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/has-jsonb-brand.type';
|
||||
|
||||
export type HasJsonbBrandInUnion<T> = T extends unknown
|
||||
? HasJsonbPropertyBrand<T>
|
||||
: never;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type JSONB_PROPERTY_BRAND } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
|
||||
|
||||
export type HasJsonbPropertyBrand<T> =
|
||||
typeof JSONB_PROPERTY_BRAND extends keyof T ? true : false;
|
||||
+11
-4
@@ -7,7 +7,8 @@ import { type ExtractEntityRelatedEntityProperties } from 'src/engine/metadata-m
|
||||
import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type';
|
||||
import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type';
|
||||
import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
import { type AllJsonbPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-by-metadata-name.constant';
|
||||
import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant';
|
||||
import { type ContainsSerializedRelation } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/contains-serialized-relation.type';
|
||||
import { type FormatRecordSerializedRelationProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/format-record-serialized-relation-properties.type';
|
||||
|
||||
export type UniversalSyncableFlatEntity = Omit<
|
||||
@@ -19,6 +20,7 @@ export type UniversalSyncableFlatEntity = Omit<
|
||||
|
||||
export type UniversalFlatEntityExtraProperties<
|
||||
TEntity extends SyncableEntity,
|
||||
// Required to be passed for narrowed type
|
||||
TMetadataName extends
|
||||
AllMetadataName = FromMetadataEntityToMetadataName<TEntity>,
|
||||
> = AddSuffixToEntityOneToManyProperties<TEntity, 'universalIdentifiers'> &
|
||||
@@ -29,8 +31,13 @@ export type UniversalFlatEntityExtraProperties<
|
||||
> & {
|
||||
applicationUniversalIdentifier: string;
|
||||
} & {
|
||||
[P in AllJsonbPropertiesForMetadataName<TMetadataName> &
|
||||
keyof TEntity]: FormatRecordSerializedRelationProperties<TEntity[P]>;
|
||||
[P in AllJsonbPropertiesWithSerializedPropertiesForMetadataName<TMetadataName> &
|
||||
keyof TEntity &
|
||||
string as `universal${Capitalize<P>}`]: true extends ContainsSerializedRelation<
|
||||
NonNullable<TEntity[P]>
|
||||
>
|
||||
? FormatRecordSerializedRelationProperties<TEntity[P]>
|
||||
: null;
|
||||
};
|
||||
|
||||
export type UniversalFlatEntityFrom<
|
||||
@@ -46,7 +53,7 @@ export type UniversalFlatEntityFrom<
|
||||
| ExtractEntityRelatedEntityProperties<TEntity>
|
||||
| Extract<MetadataManyToOneJoinColumn<TMetadataName>, keyof TEntity>
|
||||
| keyof CastRecordTypeOrmDatePropertiesToString<TEntity>
|
||||
| AllJsonbPropertiesForMetadataName<TMetadataName>
|
||||
| AllJsonbPropertiesWithSerializedPropertiesForMetadataName<TMetadataName>
|
||||
> &
|
||||
CastRecordTypeOrmDatePropertiesToString<TEntity> &
|
||||
UniversalFlatEntityExtraProperties<TEntity, TMetadataName>;
|
||||
|
||||
+41
-5
@@ -1,13 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a created entity 1`] = `
|
||||
{
|
||||
"createdFlatEntityMaps": {
|
||||
"byId": {
|
||||
"field-id-1": {
|
||||
"__universal": {},
|
||||
"applicationId": "application-id-1",
|
||||
"applicationUniversalIdentifier": "application-universal-identifier-1",
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||||
"defaultValue": null,
|
||||
"description": "default flat field metadata description",
|
||||
@@ -21,22 +22,30 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a crea
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "flat field metadata label",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "flatFieldMetadataName",
|
||||
"objectMetadataId": "object-metadata-id-1",
|
||||
"objectMetadataUniversalIdentifier": "object-metadata-universal-identifier-1",
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": null,
|
||||
"relationTargetObjectMetadataId": null,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": null,
|
||||
"settings": null,
|
||||
"standardId": null,
|
||||
"standardOverrides": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": "universal-identifier-1",
|
||||
"universalSettings": null,
|
||||
"updatedAt": "2024-01-01T00:00:00.000Z",
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -70,9 +79,10 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a dele
|
||||
"deletedFlatEntityMaps": {
|
||||
"byId": {
|
||||
"field-id-1": {
|
||||
"__universal": {},
|
||||
"applicationId": "application-id-1",
|
||||
"applicationUniversalIdentifier": "application-universal-identifier-1",
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||||
"defaultValue": null,
|
||||
"description": "default flat field metadata description",
|
||||
@@ -86,22 +96,30 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a dele
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "flat field metadata label",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "flatFieldMetadataName",
|
||||
"objectMetadataId": "object-metadata-id-1",
|
||||
"objectMetadataUniversalIdentifier": "object-metadata-universal-identifier-1",
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": null,
|
||||
"relationTargetObjectMetadataId": null,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": null,
|
||||
"settings": null,
|
||||
"standardId": null,
|
||||
"standardOverrides": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": "universal-identifier-1",
|
||||
"universalSettings": null,
|
||||
"updatedAt": "2024-01-01T00:00:00.000Z",
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -153,9 +171,10 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"createdFlatEntityMaps": {
|
||||
"byId": {
|
||||
"field-id-3": {
|
||||
"__universal": {},
|
||||
"applicationId": "application-id-1",
|
||||
"applicationUniversalIdentifier": "application-universal-identifier-1",
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||||
"defaultValue": null,
|
||||
"description": "default flat field metadata description",
|
||||
@@ -169,22 +188,30 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "flat field metadata label",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "flatFieldMetadataName",
|
||||
"objectMetadataId": "object-metadata-id-1",
|
||||
"objectMetadataUniversalIdentifier": "object-metadata-universal-identifier-1",
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": null,
|
||||
"relationTargetObjectMetadataId": null,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": null,
|
||||
"settings": null,
|
||||
"standardId": null,
|
||||
"standardOverrides": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": "universal-identifier-3",
|
||||
"universalSettings": null,
|
||||
"updatedAt": "2024-01-01T00:00:00.000Z",
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -200,9 +227,10 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"deletedFlatEntityMaps": {
|
||||
"byId": {
|
||||
"field-id-2": {
|
||||
"__universal": {},
|
||||
"applicationId": "application-id-1",
|
||||
"applicationUniversalIdentifier": "application-universal-identifier-1",
|
||||
"calendarViewIds": [],
|
||||
"calendarViewUniversalIdentifiers": [],
|
||||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||||
"defaultValue": null,
|
||||
"description": "default flat field metadata description",
|
||||
@@ -216,22 +244,30 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
"label": "flat field metadata label",
|
||||
"mainGroupByFieldMetadataViewIds": [],
|
||||
"mainGroupByFieldMetadataViewUniversalIdentifiers": [],
|
||||
"morphId": null,
|
||||
"name": "flatFieldMetadataName",
|
||||
"objectMetadataId": "object-metadata-id-1",
|
||||
"objectMetadataUniversalIdentifier": "object-metadata-universal-identifier-1",
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": null,
|
||||
"relationTargetObjectMetadataId": null,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": null,
|
||||
"settings": null,
|
||||
"standardId": null,
|
||||
"standardOverrides": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": "universal-identifier-2",
|
||||
"universalSettings": null,
|
||||
"updatedAt": "2024-01-01T00:00:00.000Z",
|
||||
"viewFieldIds": [],
|
||||
"viewFieldUniversalIdentifiers": [],
|
||||
"viewFilterIds": [],
|
||||
"viewFilterUniversalIdentifiers": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
|
||||
+36
@@ -30,6 +30,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
}),
|
||||
],
|
||||
metadataName: 'fieldMetadata',
|
||||
@@ -52,6 +56,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
}),
|
||||
],
|
||||
to: [],
|
||||
@@ -76,6 +84,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
isActive: false,
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
}),
|
||||
],
|
||||
to: [
|
||||
@@ -89,6 +101,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
isActive: true,
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
}),
|
||||
],
|
||||
metadataName: 'fieldMetadata',
|
||||
@@ -111,6 +127,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
isActive: true,
|
||||
}),
|
||||
getFlatFieldMetadataMock({
|
||||
@@ -122,6 +142,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
}),
|
||||
],
|
||||
to: [
|
||||
@@ -133,6 +157,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
workspaceId: 'workspace-id-1',
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
isActive: false,
|
||||
}),
|
||||
@@ -144,6 +172,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
workspaceId: 'workspace-id-1',
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
}),
|
||||
],
|
||||
@@ -167,6 +199,10 @@ describe('flatEntityDeletedCreatedUpdatedMatrixDispatcher', () => {
|
||||
workspaceId: 'workspace-id-1',
|
||||
applicationId: 'application-id-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
applicationUniversalIdentifier:
|
||||
'application-universal-identifier-1',
|
||||
objectMetadataUniversalIdentifier:
|
||||
'object-metadata-universal-identifier-1',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
}),
|
||||
],
|
||||
|
||||
+1
-17
@@ -1,17 +1,11 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action';
|
||||
import {
|
||||
WorkspaceMigrationActionExecutionException,
|
||||
WorkspaceMigrationActionExecutionExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/exceptions/workspace-migration-action-execution.exception';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type';
|
||||
import { generateColumnDefinitions } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/generate-column-definitions.util';
|
||||
import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/get-workspace-schema-context-for-migration.util';
|
||||
@@ -70,19 +64,9 @@ export class DeleteFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
universalIdentifier,
|
||||
});
|
||||
|
||||
const objectMetadataUniversalIdentifier =
|
||||
fieldMetadata.__universal?.objectMetadataUniversalIdentifier;
|
||||
|
||||
if (!isDefined(objectMetadataUniversalIdentifier)) {
|
||||
throw new WorkspaceMigrationActionExecutionException({
|
||||
message: `objectMetadataUniversalIdentifier is not defined for field metadata ${universalIdentifier}`,
|
||||
code: WorkspaceMigrationActionExecutionExceptionCode.NOT_SUPPORTED,
|
||||
});
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
universalIdentifier: objectMetadataUniversalIdentifier,
|
||||
universalIdentifier: fieldMetadata.objectMetadataUniversalIdentifier,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
|
||||
+2
-11
@@ -97,19 +97,10 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const objectMetadataUniversalIdentifier =
|
||||
currentFlatFieldMetadata.__universal?.objectMetadataUniversalIdentifier;
|
||||
|
||||
if (!isDefined(objectMetadataUniversalIdentifier)) {
|
||||
throw new WorkspaceMigrationActionExecutionException({
|
||||
message: `objectMetadataUniversalIdentifier is not defined for field metadata ${entityId}`,
|
||||
code: WorkspaceMigrationActionExecutionExceptionCode.NOT_SUPPORTED,
|
||||
});
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
universalIdentifier: objectMetadataUniversalIdentifier,
|
||||
universalIdentifier:
|
||||
currentFlatFieldMetadata.objectMetadataUniversalIdentifier,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
|
||||
@@ -25,6 +25,7 @@ export const extractRecordIdsAndDatesAsExpectAny = (
|
||||
|
||||
if (
|
||||
key.endsWith('Id') ||
|
||||
key.endsWith('UniversalIdentifier') ||
|
||||
key === 'universalIdentifier' ||
|
||||
key === 'id' ||
|
||||
key === 'updatedAt' ||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export type IsEmptyObject<T extends object> = [keyof T] extends [never]
|
||||
? true
|
||||
: false;
|
||||
@@ -0,0 +1 @@
|
||||
export type IsNever<T> = [T] extends [never] ? true : false;
|
||||
@@ -105,8 +105,10 @@ export { FILTERABLE_FIELD_TYPES } from './FilterableFieldType';
|
||||
export { FirstDayOfTheWeek } from './FirstDayOfTheWeek';
|
||||
export type { FromTo } from './FromToType';
|
||||
export { HTTPMethod } from './HttpMethod';
|
||||
export type { IsEmptyObject } from './IsEmptyObject.type';
|
||||
export type { IsEmptyRecord } from './IsEmptyRecord.type';
|
||||
export type { IsExactly } from './IsExactly';
|
||||
export type { IsNever } from './IsNever.type';
|
||||
export type { IsSerializedRelation } from './IsSerializedRelation.type';
|
||||
export type { LogicFunctionEvent } from './LogicFunctionEvent';
|
||||
export { MessageParticipantRole } from './MessageParticipantRole';
|
||||
|
||||
Reference in New Issue
Block a user