Spread in parent and requires FlatEntity.__universal (#17753)
# Introduction Requiring the spreaded `__universal` record that aggregates all the universal identifier ( relations fk and aggregators ) of an entity to its root It's blockin for https://github.com/twentyhq/twenty/pull/17687 to be finalized because if we don't we would have to migrated all related entities at once in order for them to always have the universal properties ## `resolveEntityRelationUniversalIdentifiers` Introduced `resolveEntityRelationUniversalIdentifiers` a centralized utility that resolves foreign key IDs to universal identifiers using ALL_METADATA_RELATIONS metadata. It provides strict typing for both input (foreign keys) and output (universal identifiers), with nullability dynamically inferred from entity relation types. Strictly and dynamically typed for both output and input To do so added a new type and const/runtime grain to ALL_METADATA_RELATIONS `isNullable`to many-to-one entries, derived from the entity relation property types. And fixed incorrectly typed typeorm entities ### Usage ```ts const { availabilityObjectMetadataUniversalIdentifier, frontComponentUniversalIdentifier, } = resolveEntityRelationUniversalIdentifiers({ metadataName: 'commandMenuItem', foreignKeyValues: { availabilityObjectMetadataId: createCommandMenuItemInput.availabilityObjectMetadataId, frontComponentId: createCommandMenuItemInput.frontComponentId, }, flatEntityMaps: { flatObjectMetadataMaps, flatFrontComponentMaps }, }); ```
This commit is contained in:
-2
@@ -5,7 +5,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
@@ -29,7 +28,6 @@ export class TwentyStandardApplicationService {
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
// Note: To remove and handle natively in validateBuildAndRun after favorite migration to metadata
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type AgentResponseFormat } from 'src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type ModelConfiguration } from 'src/engine/metadata-modules/ai/ai-agent/types/modelConfiguration';
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
@@ -61,6 +62,8 @@ export const createStandardAgentFlatMetadata = ({
|
||||
evaluationInputs,
|
||||
workspaceId,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
|
||||
+5
-1
@@ -1,6 +1,6 @@
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type';
|
||||
import { type AllStandardObjectIndexName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-index-name.type';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
@@ -80,11 +81,14 @@ export const createStandardIndexFlatMetadata = <
|
||||
flatIndex: {
|
||||
createdAt: now,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
indexType,
|
||||
indexWhereClause,
|
||||
isCustom: false,
|
||||
isUnique,
|
||||
objectMetadataId,
|
||||
objectMetadataUniversalIdentifier: flatObjectMetadata.universalIdentifier,
|
||||
universalIdentifier: indexDefinition.universalIdentifier,
|
||||
updatedAt: now,
|
||||
workspaceId,
|
||||
|
||||
+6
@@ -1,6 +1,7 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
@@ -51,12 +52,17 @@ export const createStandardNavigationMenuItemFlatMetadata = ({
|
||||
id: navigationMenuItemId,
|
||||
universalIdentifier: navigationMenuItemDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
userWorkspaceId: null,
|
||||
targetRecordId: null,
|
||||
targetObjectMetadataId: null,
|
||||
targetObjectMetadataUniversalIdentifier: null,
|
||||
viewId: flatView.id,
|
||||
viewUniversalIdentifier: flatView.universalIdentifier,
|
||||
folderId: null,
|
||||
folderUniversalIdentifier: null,
|
||||
name: null,
|
||||
position,
|
||||
createdAt: now,
|
||||
|
||||
+17
-1
@@ -4,6 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
|
||||
import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant';
|
||||
import { type StandardPageLayoutTabDefinition } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.types';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type StandardPageLayoutMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-page-layout-metadata-related-entity-ids.util';
|
||||
|
||||
export type CreateStandardPageLayoutTabContext = {
|
||||
@@ -33,7 +34,15 @@ export const createStandardPageLayoutTabFlatMetadata = ({
|
||||
const layoutIds = standardPageLayoutMetadataRelatedEntityIds[layoutName];
|
||||
const layout = STANDARD_PAGE_LAYOUTS[
|
||||
layoutName as keyof typeof STANDARD_PAGE_LAYOUTS
|
||||
] as { tabs: Record<string, StandardPageLayoutTabDefinition> };
|
||||
] as {
|
||||
universalIdentifier: string;
|
||||
tabs: Record<
|
||||
string,
|
||||
StandardPageLayoutTabDefinition & {
|
||||
widgets: Record<string, { universalIdentifier: string }>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
const tabDefinition = layout.tabs[tabTitle];
|
||||
|
||||
if (!isDefined(tabDefinition)) {
|
||||
@@ -42,16 +51,23 @@ export const createStandardPageLayoutTabFlatMetadata = ({
|
||||
|
||||
const tabIds = layoutIds.tabs[tabTitle];
|
||||
const widgetIds = Object.values(tabIds.widgets).map((widget) => widget.id);
|
||||
const widgetUniversalIdentifiers = Object.values(tabDefinition.widgets).map(
|
||||
(widget) => widget.universalIdentifier,
|
||||
);
|
||||
|
||||
return {
|
||||
id: tabIds.id,
|
||||
universalIdentifier: tabDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
title,
|
||||
position,
|
||||
pageLayoutId: layoutIds.id,
|
||||
pageLayoutUniversalIdentifier: layout.universalIdentifier,
|
||||
widgetIds,
|
||||
widgetUniversalIdentifiers,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
|
||||
+6
@@ -74,6 +74,11 @@ const computeRecordPageWidgets = ({
|
||||
? layoutObjectMetadataId
|
||||
: null;
|
||||
|
||||
const objectMetadataUniversalIdentifier =
|
||||
RECORD_PAGE_LAYOUT_WIDGET_TYPES.includes(widget.type)
|
||||
? (layout.objectUniversalIdentifier ?? null)
|
||||
: null;
|
||||
|
||||
allWidgets.push(
|
||||
createStandardPageLayoutWidgetFlatMetadata({
|
||||
now,
|
||||
@@ -81,6 +86,7 @@ const computeRecordPageWidgets = ({
|
||||
twentyStandardApplicationId,
|
||||
standardObjectMetadataRelatedEntityIds,
|
||||
standardPageLayoutMetadataRelatedEntityIds,
|
||||
objectMetadataUniversalIdentifier,
|
||||
context: {
|
||||
layoutName,
|
||||
tabTitle,
|
||||
|
||||
+15
@@ -1,6 +1,7 @@
|
||||
import { CalendarStartDay } from 'twenty-shared/constants';
|
||||
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
|
||||
import { v4 } from 'uuid';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
|
||||
@@ -59,6 +60,7 @@ const createWelcomeRichText = ({
|
||||
}): FlatPageLayoutWidget =>
|
||||
createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier: null,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -233,6 +235,8 @@ const createDealsByCompany = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -278,6 +282,8 @@ const createPipelineValueByStage = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -329,6 +335,8 @@ const createRevenueTimeline = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -375,6 +383,8 @@ const createOpportunitiesByOwner = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -423,6 +433,7 @@ const createStockMarketIframe = ({
|
||||
}): FlatPageLayoutWidget =>
|
||||
createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier: null,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -457,6 +468,8 @@ const createDealsCreatedThisMonth = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
@@ -507,6 +520,8 @@ const createDealValueCreatedThisMonth = ({
|
||||
|
||||
return createStandardPageLayoutWidgetFlatMetadata({
|
||||
...args,
|
||||
objectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECTS.opportunity.universalIdentifier,
|
||||
context: {
|
||||
layoutName: 'myFirstDashboard',
|
||||
tabTitle: 'tab1',
|
||||
|
||||
+19
-3
@@ -10,6 +10,7 @@ import {
|
||||
type StandardPageLayoutTabDefinition,
|
||||
type StandardPageLayoutWidgetDefinition,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.types';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type StandardObjectMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-object-metadata-related-entity-ids.util';
|
||||
import { type StandardPageLayoutMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-page-layout-metadata-related-entity-ids.util';
|
||||
|
||||
@@ -49,14 +50,25 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
|
||||
workspaceId,
|
||||
twentyStandardApplicationId,
|
||||
standardPageLayoutMetadataRelatedEntityIds,
|
||||
objectMetadataUniversalIdentifier,
|
||||
now,
|
||||
}: CreateStandardPageLayoutWidgetArgs): FlatPageLayoutWidget => {
|
||||
}: CreateStandardPageLayoutWidgetArgs & {
|
||||
objectMetadataUniversalIdentifier: string | null;
|
||||
}): FlatPageLayoutWidget => {
|
||||
const layoutIds = standardPageLayoutMetadataRelatedEntityIds[layoutName];
|
||||
const layout = STANDARD_PAGE_LAYOUTS[
|
||||
layoutName as keyof typeof STANDARD_PAGE_LAYOUTS
|
||||
] as { tabs: Record<string, StandardPageLayoutTabDefinition> };
|
||||
] as {
|
||||
tabs: Record<
|
||||
string,
|
||||
StandardPageLayoutTabDefinition & {
|
||||
universalIdentifier: string;
|
||||
}
|
||||
>;
|
||||
};
|
||||
const tabDefinition = layout.tabs[tabTitle];
|
||||
const widgetDef: StandardPageLayoutWidgetDefinition =
|
||||
layout.tabs[tabTitle].widgets[widgetName];
|
||||
tabDefinition.widgets[widgetName];
|
||||
|
||||
if (!isDefined(widgetDef)) {
|
||||
throw new Error(
|
||||
@@ -71,14 +83,18 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
|
||||
id: widgetIds.id,
|
||||
universalIdentifier: widgetDef.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
pageLayoutTabId: tabIds.id,
|
||||
pageLayoutTabUniversalIdentifier: tabDefinition.universalIdentifier,
|
||||
title,
|
||||
type,
|
||||
gridPosition,
|
||||
position,
|
||||
configuration,
|
||||
objectMetadataId,
|
||||
objectMetadataUniversalIdentifier,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
|
||||
+7
@@ -2,6 +2,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
|
||||
import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant';
|
||||
import { type StandardObjectMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-object-metadata-related-entity-ids.util';
|
||||
@@ -111,14 +112,20 @@ export const createStandardPageLayoutFlatMetadata = ({
|
||||
id: layoutIds.id,
|
||||
universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
name,
|
||||
type,
|
||||
objectMetadataId,
|
||||
objectMetadataUniversalIdentifier: objectUniversalIdentifier,
|
||||
tabIds: [],
|
||||
tabUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
defaultTabToFocusOnMobileAndSidePanelId,
|
||||
defaultTabToFocusOnMobileAndSidePanelUniversalIdentifier:
|
||||
defaultTabUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
|
||||
+6
@@ -1,6 +1,7 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { STANDARD_ROLE } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-role.constant';
|
||||
import { type AllStandardRoleName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-role-name.type';
|
||||
import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type';
|
||||
@@ -67,13 +68,18 @@ export const createStandardRoleFlatMetadata = ({
|
||||
canBeAssignedToApiKeys,
|
||||
workspaceId,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
permissionFlagIds: [],
|
||||
fieldPermissionIds: [],
|
||||
objectPermissionIds: [],
|
||||
roleTargetIds: [],
|
||||
roleTargetUniversalIdentifiers: [],
|
||||
rowLevelPermissionPredicateIds: [],
|
||||
rowLevelPermissionPredicateGroupIds: [],
|
||||
rowLevelPermissionPredicateGroupUniversalIdentifiers: [],
|
||||
rowLevelPermissionPredicateUniversalIdentifiers: [],
|
||||
};
|
||||
};
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatSkill } from 'src/engine/metadata-modules/flat-skill/types/flat-skill.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { STANDARD_SKILL } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-skill.constant';
|
||||
import { type AllStandardSkillName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-skill-name.type';
|
||||
import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type';
|
||||
@@ -39,6 +40,8 @@ export const createStandardSkillFlatMetadata = ({
|
||||
isActive: true,
|
||||
workspaceId,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
+22
-5
@@ -1,9 +1,10 @@
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { type AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
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 AllStandardObjectViewFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-view-field-name.type';
|
||||
@@ -50,12 +51,24 @@ export const createStandardViewFieldFlatMetadata = <
|
||||
twentyStandardApplicationId,
|
||||
now,
|
||||
}: CreateStandardViewFieldArgs<O, V>): FlatViewField => {
|
||||
// @ts-expect-error ignore
|
||||
const viewFieldDefinition = STANDARD_OBJECTS[objectName].views[viewName]
|
||||
.viewFields[viewFieldName] as {
|
||||
universalIdentifier: string;
|
||||
const objectDefinition = STANDARD_OBJECTS[objectName] as {
|
||||
fields: Record<
|
||||
AllStandardObjectFieldName<O>,
|
||||
{ universalIdentifier: string }
|
||||
>;
|
||||
views: Record<
|
||||
V,
|
||||
{
|
||||
universalIdentifier: string;
|
||||
viewFields: Record<string, { universalIdentifier: string }>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
const viewDefinition = objectDefinition.views[viewName];
|
||||
const viewFieldDefinition = viewDefinition.viewFields[viewFieldName];
|
||||
const fieldDefinition = objectDefinition.fields[fieldName];
|
||||
|
||||
if (!isDefined(viewFieldDefinition)) {
|
||||
throw new Error(
|
||||
`Invalid configuration ${objectName} ${viewName.toString()} ${viewFieldName}`,
|
||||
@@ -66,11 +79,15 @@ export const createStandardViewFieldFlatMetadata = <
|
||||
id: v4(),
|
||||
universalIdentifier: viewFieldDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
viewId:
|
||||
standardObjectMetadataRelatedEntityIds[objectName].views[viewName].id,
|
||||
viewUniversalIdentifier: viewDefinition.universalIdentifier,
|
||||
fieldMetadataId:
|
||||
standardObjectMetadataRelatedEntityIds[objectName].fields[fieldName].id,
|
||||
fieldMetadataUniversalIdentifier: fieldDefinition.universalIdentifier,
|
||||
position,
|
||||
isVisible,
|
||||
size,
|
||||
|
||||
+25
-6
@@ -1,9 +1,10 @@
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { type ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
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 AllStandardObjectViewFilterName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-view-filter-name.type';
|
||||
@@ -52,12 +53,25 @@ export const createStandardViewFilterFlatMetadata = <
|
||||
twentyStandardApplicationId,
|
||||
now,
|
||||
}: CreateStandardViewFilterArgs<O, V>): FlatViewFilter => {
|
||||
// @ts-expect-error ignore
|
||||
const viewFilterDefinition = STANDARD_OBJECTS[objectName].views[viewName]
|
||||
.viewFilters[viewFilterName] as {
|
||||
universalIdentifier: string;
|
||||
const objectDefinition = STANDARD_OBJECTS[objectName] as {
|
||||
fields: Record<
|
||||
AllStandardObjectFieldName<O>,
|
||||
{ universalIdentifier: string }
|
||||
>;
|
||||
views: Record<
|
||||
V,
|
||||
{
|
||||
universalIdentifier: string;
|
||||
viewFields: Record<string, { universalIdentifier: string }>;
|
||||
viewFilters: Record<string, { universalIdentifier: string }>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
const viewDefinition = objectDefinition.views[viewName];
|
||||
const viewFilterDefinition = viewDefinition.viewFilters[viewFilterName];
|
||||
const fieldDefinition = objectDefinition.fields[fieldName];
|
||||
|
||||
if (!isDefined(viewFilterDefinition)) {
|
||||
throw new Error(
|
||||
`Invalid configuration ${objectName} ${viewName.toString()} ${viewFilterName}`,
|
||||
@@ -68,15 +82,20 @@ export const createStandardViewFilterFlatMetadata = <
|
||||
id: v4(),
|
||||
universalIdentifier: viewFilterDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
viewId:
|
||||
standardObjectMetadataRelatedEntityIds[objectName].views[viewName].id,
|
||||
viewUniversalIdentifier: viewDefinition.universalIdentifier,
|
||||
fieldMetadataId:
|
||||
standardObjectMetadataRelatedEntityIds[objectName].fields[fieldName].id,
|
||||
fieldMetadataUniversalIdentifier: fieldDefinition.universalIdentifier,
|
||||
viewFilterGroupId,
|
||||
viewFilterGroupUniversalIdentifier: null,
|
||||
operand,
|
||||
value,
|
||||
subFieldName,
|
||||
viewFilterGroupId,
|
||||
positionInViewFilterGroup,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
|
||||
+9
-3
@@ -1,8 +1,9 @@
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
import { type AllStandardObjectViewGroupName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-view-group-name.type';
|
||||
import { type AllStandardObjectViewName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-view-name.type';
|
||||
@@ -39,11 +40,13 @@ export const createStandardViewGroupFlatMetadata = <
|
||||
now,
|
||||
}: CreateStandardViewGroupArgs<O, V>): FlatViewGroup => {
|
||||
// @ts-expect-error ignore
|
||||
const viewGroupDefinition = STANDARD_OBJECTS[objectName].views[viewName]
|
||||
.viewGroups[viewGroupName] as {
|
||||
const viewDefinition = STANDARD_OBJECTS[objectName].views[viewName] as {
|
||||
universalIdentifier: string;
|
||||
viewGroups: Record<string, { universalIdentifier: string }>;
|
||||
};
|
||||
|
||||
const viewGroupDefinition = viewDefinition.viewGroups[viewGroupName];
|
||||
|
||||
if (!isDefined(viewGroupDefinition)) {
|
||||
throw new Error(
|
||||
`Invalid configuration ${objectName} ${viewName.toString()} ${viewGroupName}`,
|
||||
@@ -54,9 +57,12 @@ export const createStandardViewGroupFlatMetadata = <
|
||||
id: v4(),
|
||||
universalIdentifier: viewGroupDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
viewId:
|
||||
standardObjectMetadataRelatedEntityIds[objectName].views[viewName].id,
|
||||
viewUniversalIdentifier: viewDefinition.universalIdentifier,
|
||||
isVisible,
|
||||
fieldValue,
|
||||
position,
|
||||
|
||||
+13
@@ -7,6 +7,7 @@ import { type ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.en
|
||||
import { ViewOpenRecordIn } from 'src/engine/metadata-modules/view/enums/view-open-record-in';
|
||||
import { type ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import { ViewVisibility } from 'src/engine/metadata-modules/view/enums/view-visibility.enum';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
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 AllStandardObjectViewName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-view-name.type';
|
||||
@@ -70,6 +71,8 @@ export const createStandardViewFlatMetadata = <
|
||||
|
||||
const objectMetadataId =
|
||||
standardObjectMetadataRelatedEntityIds[objectName].id;
|
||||
const objectMetadataUniversalIdentifier =
|
||||
STANDARD_OBJECTS[objectName].universalIdentifier;
|
||||
|
||||
const kanbanAggregateOperationFieldMetadataId =
|
||||
kanbanAggregateOperationFieldName
|
||||
@@ -91,9 +94,15 @@ export const createStandardViewFlatMetadata = <
|
||||
: null;
|
||||
|
||||
return {
|
||||
calendarFieldMetadataUniversalIdentifier: null,
|
||||
kanbanAggregateOperationFieldMetadataUniversalIdentifier: null,
|
||||
mainGroupByFieldMetadataUniversalIdentifier: null,
|
||||
objectMetadataUniversalIdentifier,
|
||||
id: standardObjectMetadataRelatedEntityIds[objectName].views[viewName].id,
|
||||
universalIdentifier: viewDefinition.universalIdentifier,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
name,
|
||||
@@ -114,9 +123,13 @@ export const createStandardViewFlatMetadata = <
|
||||
visibility: ViewVisibility.WORKSPACE,
|
||||
createdByUserWorkspaceId: null,
|
||||
viewFieldIds: [],
|
||||
viewFieldUniversalIdentifiers: [],
|
||||
viewFilterIds: [],
|
||||
viewFilterUniversalIdentifiers: [],
|
||||
viewGroupIds: [],
|
||||
viewGroupUniversalIdentifiers: [],
|
||||
viewFilterGroupIds: [],
|
||||
viewFilterGroupUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
|
||||
+16
@@ -10,6 +10,11 @@ import { type Observable, catchError } from 'rxjs';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { workspaceMigrationBuilderExceptionFormatter } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-builder-exception-formatter';
|
||||
import { workspaceMigrationRunnerExceptionFormatter } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-runner-exception-formatter';
|
||||
@@ -29,6 +34,17 @@ export class WorkspaceMigrationGraphqlApiExceptionInterceptor
|
||||
|
||||
return next.handle().pipe(
|
||||
catchError((error) => {
|
||||
if (error instanceof FlatEntityMapsException) {
|
||||
switch (error.code) {
|
||||
case FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND:
|
||||
throw new NotFoundError(error);
|
||||
case FlatEntityMapsExceptionCode.ENTITY_ALREADY_EXISTS:
|
||||
case FlatEntityMapsExceptionCode.ENTITY_MALFORMED:
|
||||
case FlatEntityMapsExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (error instanceof WorkspaceMigrationBuilderException) {
|
||||
workspaceMigrationBuilderExceptionFormatter(error, i18n);
|
||||
}
|
||||
|
||||
+2
@@ -32,6 +32,8 @@ type ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs = {
|
||||
[P in AllMetadataName]?: FlatEntityToCreateDeleteUpdate<P>;
|
||||
};
|
||||
isSystemBuild?: boolean;
|
||||
// TODO remove once application synchronization do not consume services atomically anymore
|
||||
// Should always be the universal workspace custom app id
|
||||
applicationUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
|
||||
+2
-6
@@ -100,12 +100,10 @@ export class FlatAgentValidatorService {
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
// TODO refactor once agent has been migrated to universal pattern
|
||||
isDefined(existingAgent.__universal) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existingAgent.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
existingAgent.__universal.applicationUniversalIdentifier,
|
||||
existingAgent.applicationUniversalIdentifier,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
@@ -154,12 +152,10 @@ export class FlatAgentValidatorService {
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
// TODO refactor once agent has been migrated to universal pattern
|
||||
isDefined(fromFlatAgent.__universal) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: fromFlatAgent.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
fromFlatAgent.__universal.applicationUniversalIdentifier,
|
||||
fromFlatAgent.applicationUniversalIdentifier,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
|
||||
+6
-11
@@ -93,12 +93,10 @@ export class FlatSkillValidatorService {
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
// TODO refactor once skill has been migrated to universal pattern
|
||||
isDefined(existingSkill.__universal) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existingSkill.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
existingSkill.__universal.applicationUniversalIdentifier,
|
||||
existingSkill.applicationUniversalIdentifier,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
@@ -151,14 +149,11 @@ export class FlatSkillValidatorService {
|
||||
(key) => key !== 'isActive',
|
||||
);
|
||||
|
||||
// TODO refactor once skill has been migrated to universal pattern
|
||||
const isTwentyStandardSkill =
|
||||
isDefined(fromFlatSkill.__universal) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: fromFlatSkill.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
fromFlatSkill.__universal.applicationUniversalIdentifier,
|
||||
});
|
||||
const isTwentyStandardSkill = belongsToTwentyStandardApp({
|
||||
universalIdentifier: fromFlatSkill.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
fromFlatSkill.applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
|
||||
Reference in New Issue
Block a user