Fix record page layout upgrade commands (#18962)
- Fix issue with name that must be first in the view field list - Improve dry run and logs of backfill page layouts command --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: prastoin <[email protected]>
This commit is contained in:
co-authored by
Copilot
prastoin
parent
4d6c8db205
commit
cc247c2e8e
+110
-38
@@ -68,19 +68,46 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
this.logger.log(
|
||||
`[DRY RUN] Would create RECORD_PAGE page layouts and enable IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { twentyStandardFlatApplication, workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
await this.backfillStandardObjectPageLayouts({
|
||||
workspaceId,
|
||||
twentyStandardFlatApplication,
|
||||
isDryRun,
|
||||
});
|
||||
|
||||
await this.backfillCustomObjectPageLayouts({
|
||||
workspaceId,
|
||||
workspaceCustomFlatApplication,
|
||||
isDryRun,
|
||||
});
|
||||
|
||||
if (!isDryRun) {
|
||||
await this.featureFlagService.enableFeatureFlags(
|
||||
[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
isDryRun
|
||||
? `[DRY RUN] Would create page layouts and enable IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED for workspace ${workspaceId}`
|
||||
: `Successfully created page layouts and enabled IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async backfillStandardObjectPageLayouts({
|
||||
workspaceId,
|
||||
twentyStandardFlatApplication,
|
||||
isDryRun,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
twentyStandardFlatApplication: FlatApplication;
|
||||
isDryRun: boolean;
|
||||
}): Promise<void> {
|
||||
const { allFlatEntityMaps: standardAllFlatEntityMaps } =
|
||||
computeTwentyStandardApplicationAllFlatEntityMaps({
|
||||
shouldIncludeRecordPageLayouts: true,
|
||||
@@ -89,6 +116,22 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
twentyStandardApplicationId: twentyStandardFlatApplication.id,
|
||||
});
|
||||
|
||||
const {
|
||||
flatPageLayoutMaps: existingFlatPageLayoutMaps,
|
||||
flatPageLayoutTabMaps: existingFlatPageLayoutTabMaps,
|
||||
flatPageLayoutWidgetMaps: existingFlatPageLayoutWidgetMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
|
||||
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatPageLayoutMaps',
|
||||
'flatPageLayoutTabMaps',
|
||||
'flatPageLayoutWidgetMaps',
|
||||
'flatViewMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewFieldGroupMaps',
|
||||
]);
|
||||
|
||||
const recordPageLayoutUniversalIdentifiers = new Set<string>();
|
||||
|
||||
const pageLayoutsToCreate = Object.values(
|
||||
@@ -104,6 +147,16 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
pageLayout.universalIdentifier,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
existingFlatPageLayoutMaps.byUniversalIdentifier[
|
||||
pageLayout.universalIdentifier
|
||||
],
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -124,6 +177,16 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
|
||||
tabUniversalIdentifiers.add(tab.universalIdentifier);
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
existingFlatPageLayoutTabMaps.byUniversalIdentifier[
|
||||
tab.universalIdentifier
|
||||
],
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -131,20 +194,18 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
standardAllFlatEntityMaps.flatPageLayoutWidgetMaps.byUniversalIdentifier,
|
||||
)
|
||||
.filter(isDefined)
|
||||
.filter((widget) =>
|
||||
tabUniversalIdentifiers.has(widget.pageLayoutTabUniversalIdentifier),
|
||||
.filter(
|
||||
(widget) =>
|
||||
tabUniversalIdentifiers.has(
|
||||
widget.pageLayoutTabUniversalIdentifier,
|
||||
) &&
|
||||
!isDefined(
|
||||
existingFlatPageLayoutWidgetMaps.byUniversalIdentifier[
|
||||
widget.universalIdentifier
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
const {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewFieldMaps: existingFlatViewFieldMaps,
|
||||
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
|
||||
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatViewMaps',
|
||||
'flatViewFieldMaps',
|
||||
'flatViewFieldGroupMaps',
|
||||
]);
|
||||
|
||||
const viewUniversalIdentifiers = new Set<string>();
|
||||
|
||||
const viewsToCreate = Object.values(
|
||||
@@ -201,6 +262,20 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`${isDryRun ? '[DRY RUN] ' : ''}Found standard entities to create for workspace ${workspaceId}: ` +
|
||||
`${pageLayoutsToCreate.length} page layout(s), ` +
|
||||
`${pageLayoutTabsToCreate.length} tab(s), ` +
|
||||
`${pageLayoutWidgetsToCreate.length} widget(s), ` +
|
||||
`${viewsToCreate.length} view(s), ` +
|
||||
`${viewFieldsToCreate.length} view field(s), ` +
|
||||
`${viewFieldGroupsToCreate.length} view field group(s)`,
|
||||
);
|
||||
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
@@ -250,28 +325,16 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
`Failed to create standard page layouts for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.backfillCustomObjectPageLayouts({
|
||||
workspaceId,
|
||||
workspaceCustomFlatApplication,
|
||||
});
|
||||
|
||||
await this.featureFlagService.enableFeatureFlags(
|
||||
[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Successfully created page layouts and enabled IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async backfillCustomObjectPageLayouts({
|
||||
workspaceId,
|
||||
workspaceCustomFlatApplication,
|
||||
isDryRun,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
workspaceCustomFlatApplication: FlatApplication;
|
||||
isDryRun: boolean;
|
||||
}): Promise<void> {
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
@@ -316,10 +379,6 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Creating page layouts for ${customObjectsWithoutPageLayout.length} custom object(s) in workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
const allCustomPageLayoutsToCreate: FlatPageLayout[] = [];
|
||||
const allCustomPageLayoutTabsToCreate: FlatPageLayoutTab[] = [];
|
||||
const allCustomPageLayoutWidgetsToCreate: FlatPageLayoutWidget[] = [];
|
||||
@@ -361,6 +420,19 @@ export class BackfillPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigra
|
||||
allCustomViewFieldsToCreate.push(...viewFields);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`${isDryRun ? '[DRY RUN] ' : ''}Found custom entities to create for ${customObjectsWithoutPageLayout.length} custom object(s) in workspace ${workspaceId}: ` +
|
||||
`${allCustomPageLayoutsToCreate.length} page layout(s), ` +
|
||||
`${allCustomPageLayoutTabsToCreate.length} tab(s), ` +
|
||||
`${allCustomPageLayoutWidgetsToCreate.length} widget(s), ` +
|
||||
`${allCustomViewsToCreate.length} view(s), ` +
|
||||
`${allCustomViewFieldsToCreate.length} view field(s)`,
|
||||
);
|
||||
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customValidateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
|
||||
+17
-38
@@ -4,9 +4,9 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
ViewKey,
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewKey,
|
||||
ViewVisibility,
|
||||
} from 'twenty-shared/types';
|
||||
import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils';
|
||||
@@ -24,7 +24,6 @@ import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
|
||||
import { NavigationMenuItemType } from 'src/engine/metadata-modules/navigation-menu-item/enums/navigation-menu-item-type.enum';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util';
|
||||
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
|
||||
@@ -32,6 +31,7 @@ import { fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities } from
|
||||
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
|
||||
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
|
||||
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { NavigationMenuItemType } from 'src/engine/metadata-modules/navigation-menu-item/enums/navigation-menu-item-type.enum';
|
||||
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
||||
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
||||
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
@@ -414,14 +414,13 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
flatApplication: resolvedOwnerFlatApplication,
|
||||
});
|
||||
|
||||
const flatDefaultViewFieldsToCreate =
|
||||
await this.computeFlatViewFieldsToCreate({
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
objectFlatFieldMetadatas: flatFieldMetadataToCreateOnObject,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
flatObjectMetadataToCreate.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
viewUniversalIdentifier: flatDefaultViewToCreate.universalIdentifier,
|
||||
});
|
||||
const flatDefaultViewFieldsToCreate = computeFlatViewFieldsToCreate({
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
objectFlatFieldMetadatas: flatFieldMetadataToCreateOnObject,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
flatObjectMetadataToCreate.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
viewUniversalIdentifier: flatDefaultViewToCreate.universalIdentifier,
|
||||
});
|
||||
|
||||
let flatRecordPageFieldsViewToCreate:
|
||||
| (UniversalFlatView & { id: string })
|
||||
@@ -449,15 +448,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
flatApplication: resolvedOwnerFlatApplication,
|
||||
});
|
||||
|
||||
flatRecordPageFieldsViewFieldsToCreate =
|
||||
await this.computeFlatViewFieldsToCreate({
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
objectFlatFieldMetadatas: flatFieldMetadataToCreateOnObject,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
flatObjectMetadataToCreate.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
viewUniversalIdentifier:
|
||||
flatRecordPageFieldsViewToCreate.universalIdentifier,
|
||||
});
|
||||
flatRecordPageFieldsViewFieldsToCreate = computeFlatViewFieldsToCreate({
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
objectFlatFieldMetadatas: flatFieldMetadataToCreateOnObject,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
flatObjectMetadataToCreate.labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
viewUniversalIdentifier:
|
||||
flatRecordPageFieldsViewToCreate.universalIdentifier,
|
||||
});
|
||||
|
||||
flatDefaultRecordPageLayoutsToCreate =
|
||||
this.computeFlatDefaultRecordPageLayoutToCreate({
|
||||
@@ -664,25 +662,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
});
|
||||
}
|
||||
|
||||
private async computeFlatViewFieldsToCreate({
|
||||
objectFlatFieldMetadatas,
|
||||
viewUniversalIdentifier,
|
||||
flatApplication,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
}: {
|
||||
flatApplication: FlatApplication;
|
||||
objectFlatFieldMetadatas: UniversalFlatFieldMetadata[];
|
||||
viewUniversalIdentifier: string;
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: string | null;
|
||||
}) {
|
||||
return computeFlatViewFieldsToCreate({
|
||||
objectFlatFieldMetadatas,
|
||||
viewUniversalIdentifier,
|
||||
flatApplication,
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
private async computeFlatNavigationMenuItemToCreate({
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
|
||||
+15
@@ -29,6 +29,21 @@ export const computeFlatViewFieldsToCreate = ({
|
||||
field.universalIdentifier ===
|
||||
labelIdentifierFieldMetadataUniversalIdentifier),
|
||||
)
|
||||
.sort((a, b) => {
|
||||
const aIsLabelIdentifierFieldMetadata =
|
||||
a.universalIdentifier ===
|
||||
labelIdentifierFieldMetadataUniversalIdentifier;
|
||||
const bIsLabelIdentifierFieldMetadata =
|
||||
b.universalIdentifier ===
|
||||
labelIdentifierFieldMetadataUniversalIdentifier;
|
||||
|
||||
if (aIsLabelIdentifierFieldMetadata && !bIsLabelIdentifierFieldMetadata)
|
||||
return -1;
|
||||
if (!aIsLabelIdentifierFieldMetadata && bIsLabelIdentifierFieldMetadata)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
})
|
||||
.map<UniversalFlatViewField>((field, index) => ({
|
||||
fieldMetadataUniversalIdentifier: field.universalIdentifier,
|
||||
viewUniversalIdentifier,
|
||||
|
||||
Reference in New Issue
Block a user