Message campaign backfill method sort view field (#23433)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23433?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Paul Rastoin
2026-07-28 16:07:52 +02:00
committed by GitHub
parent 8211206187
commit 1fb1232a17
@@ -13,6 +13,7 @@ import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules
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';
import { type FlatSearchFieldMetadata } from 'src/engine/metadata-modules/flat-search-field-metadata/types/flat-search-field-metadata.type';
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant';
@@ -56,11 +57,13 @@ export class AddMessageCampaignNameFieldCommand extends ProvisionedWorkspaceComm
const {
flatObjectMetadataMaps,
flatFieldMetadataMaps,
flatViewMaps,
flatViewFieldMaps,
flatSearchFieldMetadataMaps,
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
'flatViewMaps',
'flatViewFieldMaps',
'flatSearchFieldMetadataMaps',
]);
@@ -115,6 +118,26 @@ export class AddMessageCampaignNameFieldCommand extends ProvisionedWorkspaceComm
fieldsToCreate.push(standardNameField);
}
// Move the label identifier from subject to name, but never clobber a
// user customization pointing at another field.
const currentLabelIdentifier =
campaignObjectMetadata.labelIdentifierFieldMetadataUniversalIdentifier;
const isNameLabelIdentifier =
!isDefined(currentLabelIdentifier) ||
currentLabelIdentifier === SUBJECT_FIELD_UNIVERSAL_IDENTIFIER ||
currentLabelIdentifier === NAME_FIELD_UNIVERSAL_IDENTIFIER;
const flatObjectMetadataToUpdate: FlatObjectMetadata[] =
isNameLabelIdentifier &&
currentLabelIdentifier !== NAME_FIELD_UNIVERSAL_IDENTIFIER
? [
{
...campaignObjectMetadata,
labelIdentifierFieldMetadataUniversalIdentifier:
NAME_FIELD_UNIVERSAL_IDENTIFIER,
},
]
: [];
const viewFieldsToCreate: FlatViewField[] = [];
if (
@@ -136,7 +159,15 @@ export class AddMessageCampaignNameFieldCommand extends ProvisionedWorkspaceComm
);
}
viewFieldsToCreate.push(standardNameViewField);
viewFieldsToCreate.push({
...standardNameViewField,
position: this.computeNameViewFieldPosition({
flatViewMaps,
flatViewFieldMaps,
standardNameViewField,
isNameLabelIdentifier,
}),
});
}
const searchFieldMetadatasToCreate =
@@ -148,22 +179,6 @@ export class AddMessageCampaignNameFieldCommand extends ProvisionedWorkspaceComm
twentyStandardFlatApplication.universalIdentifier,
});
// Move the label identifier from subject to name, but never clobber a
// user customization pointing at another field.
const currentLabelIdentifier =
campaignObjectMetadata.labelIdentifierFieldMetadataUniversalIdentifier;
const flatObjectMetadataToUpdate: FlatObjectMetadata[] =
!isDefined(currentLabelIdentifier) ||
currentLabelIdentifier === SUBJECT_FIELD_UNIVERSAL_IDENTIFIER
? [
{
...campaignObjectMetadata,
labelIdentifierFieldMetadataUniversalIdentifier:
NAME_FIELD_UNIVERSAL_IDENTIFIER,
},
]
: [];
const totalOperationCount =
fieldsToCreate.length +
viewFieldsToCreate.length +
@@ -233,6 +248,49 @@ export class AddMessageCampaignNameFieldCommand extends ProvisionedWorkspaceComm
);
}
// The standard positions assume a freshly provisioned view, where name comes
// first and everything else is shifted by one. Existing views keep subject at
// position 0, so name is placed relative to the columns already there: below
// all of them when it becomes the label identifier (the validator requires
// it), above all of them otherwise (the validator forbids anything below the
// label identifier).
private computeNameViewFieldPosition({
flatViewMaps,
flatViewFieldMaps,
standardNameViewField,
isNameLabelIdentifier,
}: {
flatViewMaps: FlatEntityMaps<FlatView>;
flatViewFieldMaps: FlatEntityMaps<FlatViewField>;
standardNameViewField: FlatViewField;
isNameLabelIdentifier: boolean;
}): number {
const flatView = findFlatEntityByUniversalIdentifier<FlatView>({
flatEntityMaps: flatViewMaps,
universalIdentifier: standardNameViewField.viewUniversalIdentifier,
});
if (!isDefined(flatView)) {
return standardNameViewField.position;
}
const otherViewFieldPositions = flatView.viewFieldUniversalIdentifiers
.map(
(viewFieldUniversalIdentifier) =>
flatViewFieldMaps.byUniversalIdentifier[viewFieldUniversalIdentifier],
)
.filter(isDefined)
.map(({ position }) => position);
if (otherViewFieldPositions.length === 0) {
return standardNameViewField.position;
}
return isNameLabelIdentifier
? Math.min(...otherViewFieldPositions) - 1
: Math.max(...otherViewFieldPositions) + 1;
}
private computeSearchFieldMetadatasToCreate({
flatSearchFieldMetadataMaps,
standardFlatSearchFieldMetadataMaps,