From 2e1117d4425262f529bb0228d962bc258c78f4d8 Mon Sep 17 00:00:00 2001 From: neo773 <62795688+neo773@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:17:52 +0530 Subject: [PATCH] feat(messaging): message campaign delivery stats + views (#22452) Review in cubic --- .../2-19-upgrade-version-command.module.ts | 6 - ...rop-metadata-standard-overrides-column.ts} | 2 +- .../2-20-upgrade-version-command.module.ts | 30 + ...dd-message-campaign-stat-fields.command.ts | 231 +++ ...423001-create-message-list-view.command.ts | 187 ++ ...ic-field-universal-identifiers.command.ts} | 4 +- ...ssage-campaign-stat-fields.command.spec.ts | 234 +++ ...1-create-message-list-view.command.spec.ts | 202 +++ ...-overrides-column.instance-command.spec.ts | 4 +- ...es-column-upgrade-command-name.constant.ts | 2 +- .../instance-commands.constant.ts | 2 +- .../workspace-command-provider.module.ts | 2 + .../types/cache-storage-namespace.enum.ts | 1 + .../constants/campaign.constant.ts | 5 + .../refresh-campaign-stats-job-data.type.ts | 4 + .../core-modules/message-queue/jobs.module.ts | 2 + ...adata-related-entity-ids.util.spec.ts.snap | 1563 +++++++++-------- ...paign-standard-flat-field-metadata.util.ts | 52 + ...dard-flat-view-field-metadata-maps.util.ts | 4 + ...ndard-message-campaign-view-fields.util.ts | 144 ++ ...-standard-message-list-view-fields.util.ts | 60 + ...d-standard-flat-view-metadata-maps.util.ts | 4 + ...te-standard-message-campaign-views.util.ts | 26 + ...ompute-standard-message-list-views.util.ts | 26 + .../src/modules/emailing/emailing.module.ts | 5 + .../jobs/refresh-campaign-stats.job.ts | 21 + .../resolvers/emailing-send.resolver.ts | 2 + .../message-campaign-statistics.service.ts | 75 + .../services/message-campaign.service.ts | 74 +- .../message-campaign.workspace-entity.ts | 4 + ...ardObjectUniversalIdentifiers.test.ts.snap | 71 + .../constants/standard-object.constant.ts | 71 + 32 files changed, 2361 insertions(+), 759 deletions(-) rename packages/twenty-server/src/database/commands/upgrade-version-command/2-20/{2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column.ts => 2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column.ts} (96%) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-upgrade-version-command.module.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command.ts rename packages/twenty-server/src/database/commands/upgrade-version-command/{2-19/2-19-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts => 2-20/2-20-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts} (99%) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.spec.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423001-create-message-list-view.command.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-campaign-view-fields.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-list-view-fields.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-campaign-views.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-list-views.util.ts create mode 100644 packages/twenty-server/src/modules/emailing/jobs/refresh-campaign-stats.job.ts create mode 100644 packages/twenty-server/src/modules/emailing/services/message-campaign-statistics.service.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-upgrade-version-command.module.ts index 1af0474d49..659f4e0e6f 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-upgrade-version-command.module.ts @@ -4,16 +4,13 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; import { BackfillWorkspaceCustomApplicationRegistrationCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1782853718000-backfill-workspace-custom-application-registration.command'; import { BackfillSystemUniqueIndexUniversalIdentifierCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1783093620000-backfill-system-unique-index-universal-identifier.command'; -import { BackfillDeterministicFieldUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command'; import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; -import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/workspace-migration-runner.module'; @Module({ imports: [ @@ -21,19 +18,16 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor TypeOrmModule.forFeature([ WorkspaceEntity, ApplicationEntity, - FieldMetadataEntity, IndexMetadataEntity, ]), WorkspaceIteratorModule, WorkspaceCacheModule, WorkspaceMetadataVersionModule, WorkspaceMigrationModule, - WorkspaceMigrationRunnerModule, ], providers: [ BackfillWorkspaceCustomApplicationRegistrationCommand, BackfillSystemUniqueIndexUniversalIdentifierCommand, - BackfillDeterministicFieldUniversalIdentifiersCommand, ], }) export class V2_19_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column.ts similarity index 96% rename from packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column.ts rename to packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column.ts index 10146c66d5..d0da726639 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column.ts @@ -8,7 +8,7 @@ import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/ // (the sequence only runs previous + current versions). down() restores it. const TABLES = ['objectMetadata', 'fieldMetadata'] as const; -@RegisteredInstanceCommand('2.20.0', 1825000000000) +@RegisteredInstanceCommand('2.20.0', 1782991100000) export class DropMetadataStandardOverridesColumnFastInstanceCommand implements FastInstanceCommand { diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-upgrade-version-command.module.ts new file mode 100644 index 0000000000..4d0a6e4ac1 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-upgrade-version-command.module.ts @@ -0,0 +1,30 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; +import { AddMessageCampaignStatFieldsCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command'; +import { CreateMessageListViewCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command'; +import { BackfillDeterministicFieldUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; +import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; +import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; +import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/workspace-migration-runner.module'; + +@Module({ + imports: [ + ApplicationModule, + TypeOrmModule.forFeature([ApplicationEntity, FieldMetadataEntity]), + WorkspaceIteratorModule, + WorkspaceCacheModule, + WorkspaceMigrationModule, + WorkspaceMigrationRunnerModule, + ], + providers: [ + AddMessageCampaignStatFieldsCommand, + CreateMessageListViewCommand, + BackfillDeterministicFieldUniversalIdentifiersCommand, + ], +}) +export class V2_20_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.ts new file mode 100644 index 0000000000..675c2b924a --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.ts @@ -0,0 +1,231 @@ +import { Command } from 'nest-commander'; +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { isDefined } from 'twenty-shared/utils'; + +import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; +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 FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; +import { type TwentyStandardAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/types/twenty-standard-all-flat-entity-maps.type'; +import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { type WorkspaceCacheDataMap } from 'src/engine/workspace-cache/types/workspace-cache-key.type'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +const CAMPAIGN = STANDARD_OBJECTS.messageCampaign; + +const STAT_FIELD_UNIVERSAL_IDENTIFIERS = [ + CAMPAIGN.fields.sentCount.universalIdentifier, + CAMPAIGN.fields.failedCount.universalIdentifier, + CAMPAIGN.fields.bouncedCount.universalIdentifier, + CAMPAIGN.fields.complainedCount.universalIdentifier, +]; + +// The allMessageCampaigns view is introduced by this feature, so existing +// workspaces have no messageCampaign view at all. Create the view and every one +// of its columns (not just the stat columns) so it materializes end to end. +const CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER = + CAMPAIGN.views.allMessageCampaigns.universalIdentifier; + +const CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS = Object.values( + CAMPAIGN.views.allMessageCampaigns.viewFields, +).map((viewField) => viewField.universalIdentifier); + +@RegisteredWorkspaceCommand('2.20.0', 1783083423000) +@Command({ + name: 'upgrade:2-20:add-message-campaign-stat-fields', + description: + 'Add the MessageCampaign delivery-stat fields (sent/failed/bounced/complained) and their view columns on existing workspaces', +}) +export class AddMessageCampaignStatFieldsCommand extends ActiveOrSuspendedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly applicationService: ApplicationService, + private readonly workspaceCacheService: WorkspaceCacheService, + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const { + flatFieldMetadataMaps, + flatObjectMetadataMaps, + flatViewMaps, + flatViewFieldMaps, + } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatFieldMetadataMaps', + 'flatObjectMetadataMaps', + 'flatViewMaps', + 'flatViewFieldMaps', + ]); + + const campaignObjectMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatObjectMetadataMaps, + universalIdentifier: CAMPAIGN.universalIdentifier, + }); + + if (!isDefined(campaignObjectMetadata)) { + this.logger.log( + `messageCampaign object does not exist for workspace ${workspaceId}, skipping`, + ); + + return; + } + + const { twentyStandardFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { allFlatEntityMaps: standardAllFlatEntityMaps } = + computeTwentyStandardApplicationAllFlatEntityMaps({ + now: new Date().toISOString(), + workspaceId, + twentyStandardApplicationId: twentyStandardFlatApplication.id, + }); + + const fieldsToCreate = STAT_FIELD_UNIVERSAL_IDENTIFIERS.filter( + (universalIdentifier) => + !isDefined( + flatFieldMetadataMaps.byUniversalIdentifier[universalIdentifier], + ), + ).map((universalIdentifier) => { + const standardField = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: standardAllFlatEntityMaps.flatFieldMetadataMaps, + universalIdentifier, + }); + + if (!isDefined(standardField)) { + throw new Error( + `Standard application is missing messageCampaign field ${universalIdentifier}`, + ); + } + + return standardField; + }); + + const viewsToCreate = this.resolveViewsToCreate({ + flatViewMaps, + standardAllFlatEntityMaps, + }); + + const viewFieldsToCreate = CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.filter( + (universalIdentifier) => + !isDefined(flatViewFieldMaps.byUniversalIdentifier[universalIdentifier]), + ).map((universalIdentifier) => { + const standardViewField = findFlatEntityByUniversalIdentifier( + { + flatEntityMaps: standardAllFlatEntityMaps.flatViewFieldMaps, + universalIdentifier, + }, + ); + + if (!isDefined(standardViewField)) { + throw new Error( + `Standard application is missing messageCampaign view column ${universalIdentifier}`, + ); + } + + return standardViewField; + }); + + const hasMetadataChanges = + fieldsToCreate.length > 0 || + viewsToCreate.length > 0 || + viewFieldsToCreate.length > 0; + + if (isDryRun) { + this.logger.log( + `[DRY RUN] Workspace ${workspaceId}: ${fieldsToCreate.length} field(s), ${viewsToCreate.length} view(s), ${viewFieldsToCreate.length} view column(s)`, + ); + + return; + } + + if (hasMetadataChanges) { + const result = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + isSystemBuild: true, + workspaceId, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, + allFlatEntityOperationByMetadataName: { + fieldMetadata: { + flatEntityToCreate: fieldsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + view: { + flatEntityToCreate: viewsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + viewField: { + flatEntityToCreate: viewFieldsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + }, + ); + + if (result.status === 'fail') { + this.logger.error( + `Failed to add messageCampaign stat fields:\n${JSON.stringify(result, null, 2)}`, + ); + + throw new Error( + `Failed to add messageCampaign stat fields for workspace ${workspaceId}`, + ); + } + } + + this.logger.log( + `Applied messageCampaign stat fields for workspace ${workspaceId}`, + ); + } + + private resolveViewsToCreate({ + flatViewMaps, + standardAllFlatEntityMaps, + }: { + flatViewMaps: WorkspaceCacheDataMap['flatViewMaps']; + standardAllFlatEntityMaps: TwentyStandardAllFlatEntityMaps; + }): FlatView[] { + if ( + isDefined( + flatViewMaps.byUniversalIdentifier[CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER], + ) + ) { + return []; + } + + const standardView = findFlatEntityByUniversalIdentifier({ + flatEntityMaps: standardAllFlatEntityMaps.flatViewMaps, + universalIdentifier: CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER, + }); + + if (!isDefined(standardView)) { + throw new Error( + `Standard application is missing messageCampaign view ${CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER}`, + ); + } + + return [standardView]; + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command.ts new file mode 100644 index 0000000000..6f7950f8b4 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command.ts @@ -0,0 +1,187 @@ +import { Command } from 'nest-commander'; +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { isDefined } from 'twenty-shared/utils'; + +import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; +import { type TwentyStandardAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/types/twenty-standard-all-flat-entity-maps.type'; +import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { type WorkspaceCacheDataMap } from 'src/engine/workspace-cache/types/workspace-cache-key.type'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +const LIST = STANDARD_OBJECTS.messageList; + +const LIST_VIEW_UNIVERSAL_IDENTIFIER = + LIST.views.allMessageLists.universalIdentifier; + +const LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS = Object.values( + LIST.views.allMessageLists.viewFields, +).map((viewField) => viewField.universalIdentifier); + +@RegisteredWorkspaceCommand('2.20.0', 1783083423001) +@Command({ + name: 'upgrade:2-20:create-message-list-view', + description: + 'Create the MessageList standard view and its columns on existing workspaces', +}) +export class CreateMessageListViewCommand extends ActiveOrSuspendedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly applicationService: ApplicationService, + private readonly workspaceCacheService: WorkspaceCacheService, + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const { flatObjectMetadataMaps, flatViewMaps, flatViewFieldMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatObjectMetadataMaps', + 'flatViewMaps', + 'flatViewFieldMaps', + ]); + + const listObjectMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatObjectMetadataMaps, + universalIdentifier: LIST.universalIdentifier, + }); + + if (!isDefined(listObjectMetadata)) { + this.logger.log( + `messageList object does not exist for workspace ${workspaceId}, skipping`, + ); + + return; + } + + const { twentyStandardFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { allFlatEntityMaps: standardAllFlatEntityMaps } = + computeTwentyStandardApplicationAllFlatEntityMaps({ + now: new Date().toISOString(), + workspaceId, + twentyStandardApplicationId: twentyStandardFlatApplication.id, + }); + + const viewsToCreate = this.resolveViewsToCreate({ + flatViewMaps, + standardAllFlatEntityMaps, + }); + + const viewFieldsToCreate = LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.filter( + (universalIdentifier) => + !isDefined(flatViewFieldMaps.byUniversalIdentifier[universalIdentifier]), + ).map((universalIdentifier) => { + const standardViewField = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: standardAllFlatEntityMaps.flatViewFieldMaps, + universalIdentifier, + }); + + if (!isDefined(standardViewField)) { + throw new Error( + `Standard application is missing messageList view column ${universalIdentifier}`, + ); + } + + return standardViewField; + }); + + if (viewsToCreate.length === 0 && viewFieldsToCreate.length === 0) { + this.logger.log( + `messageList view already exists for workspace ${workspaceId}, skipping`, + ); + + return; + } + + if (isDryRun) { + this.logger.log( + `[DRY RUN] Workspace ${workspaceId}: ${viewsToCreate.length} view(s), ${viewFieldsToCreate.length} view column(s)`, + ); + + return; + } + + const result = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + isSystemBuild: true, + workspaceId, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, + allFlatEntityOperationByMetadataName: { + view: { + flatEntityToCreate: viewsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + viewField: { + flatEntityToCreate: viewFieldsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + }, + ); + + if (result.status === 'fail') { + this.logger.error( + `Failed to create messageList view:\n${JSON.stringify(result, null, 2)}`, + ); + + throw new Error( + `Failed to create messageList view for workspace ${workspaceId}`, + ); + } + + this.logger.log(`Created messageList view for workspace ${workspaceId}`); + } + + private resolveViewsToCreate({ + flatViewMaps, + standardAllFlatEntityMaps, + }: { + flatViewMaps: WorkspaceCacheDataMap['flatViewMaps']; + standardAllFlatEntityMaps: TwentyStandardAllFlatEntityMaps; + }): FlatView[] { + if ( + isDefined( + flatViewMaps.byUniversalIdentifier[LIST_VIEW_UNIVERSAL_IDENTIFIER], + ) + ) { + return []; + } + + const standardView = findFlatEntityByUniversalIdentifier({ + flatEntityMaps: standardAllFlatEntityMaps.flatViewMaps, + universalIdentifier: LIST_VIEW_UNIVERSAL_IDENTIFIER, + }); + + if (!isDefined(standardView)) { + throw new Error( + `Standard application is missing messageList view ${LIST_VIEW_UNIVERSAL_IDENTIFIER}`, + ); + } + + return [standardView]; + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts similarity index 99% rename from packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts rename to packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts index 9280939397..7eb7aeb9c2 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783100000000-backfill-deterministic-field-universal-identifiers.command.ts @@ -87,9 +87,9 @@ const isMorphOrRelationFieldMetadataType = (type: FieldMetadataType) => type === FieldMetadataType.RELATION || type === FieldMetadataType.MORPH_RELATION; -@RegisteredWorkspaceCommand('2.19.0', 1783100000000) +@RegisteredWorkspaceCommand('2.20.0', 1783100000000) @Command({ - name: 'upgrade:2-19:backfill-deterministic-field-universal-identifiers', + name: 'upgrade:2-20:backfill-deterministic-field-universal-identifiers', description: 'Recompute the universal identifier of auto-provisioned field metadata (system fields and default relation fields) to the deterministic getFieldUniversalIdentifier derivation.', }) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.spec.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.spec.ts new file mode 100644 index 0000000000..054db5ca44 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command.spec.ts @@ -0,0 +1,234 @@ +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; + +import { type WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { AddMessageCampaignStatFieldsCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423000-add-message-campaign-stat-fields.command'; +import { type ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { type WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { type WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +jest.mock( + 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant', +); + +const computeTwentyStandardApplicationAllFlatEntityMapsMock = + computeTwentyStandardApplicationAllFlatEntityMaps as jest.Mock; + +const WORKSPACE_ID = '20202020-0000-0000-0000-000000000001'; +const STANDARD_APPLICATION = { + id: '20202020-0000-0000-0000-0000000000aa', + universalIdentifier: '20202020-0000-0000-0000-0000000000bb', +}; + +const CAMPAIGN = STANDARD_OBJECTS.messageCampaign; +const STAT_FIELD_UNIVERSAL_IDENTIFIERS = [ + CAMPAIGN.fields.sentCount.universalIdentifier, + CAMPAIGN.fields.failedCount.universalIdentifier, + CAMPAIGN.fields.bouncedCount.universalIdentifier, + CAMPAIGN.fields.complainedCount.universalIdentifier, +]; +const CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER = + CAMPAIGN.views.allMessageCampaigns.universalIdentifier; +const CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS = Object.values( + CAMPAIGN.views.allMessageCampaigns.viewFields, +).map((viewField) => viewField.universalIdentifier); + +const buildByUniversalIdentifierMap = (universalIdentifiers: string[]) => ({ + byUniversalIdentifier: Object.fromEntries( + universalIdentifiers.map((universalIdentifier) => [ + universalIdentifier, + { universalIdentifier }, + ]), + ), +}); + +describe('AddMessageCampaignStatFieldsCommand', () => { + let command: AddMessageCampaignStatFieldsCommand; + let findApplicationMock: jest.Mock; + let getOrRecomputeMock: jest.Mock; + let validateBuildAndRunWorkspaceMigrationMock: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + + findApplicationMock = jest.fn().mockResolvedValue({ + twentyStandardFlatApplication: STANDARD_APPLICATION, + }); + getOrRecomputeMock = jest.fn(); + validateBuildAndRunWorkspaceMigrationMock = jest + .fn() + .mockResolvedValue({ status: 'success' }); + + computeTwentyStandardApplicationAllFlatEntityMapsMock.mockReturnValue({ + allFlatEntityMaps: { + flatFieldMetadataMaps: buildByUniversalIdentifierMap( + STAT_FIELD_UNIVERSAL_IDENTIFIERS, + ), + flatViewMaps: buildByUniversalIdentifierMap([ + CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER, + ]), + flatViewFieldMaps: buildByUniversalIdentifierMap( + CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS, + ), + }, + }); + + command = new AddMessageCampaignStatFieldsCommand( + {} as WorkspaceIteratorService, + { + findWorkspaceTwentyStandardAndCustomApplicationOrThrow: + findApplicationMock, + } as unknown as ApplicationService, + { + getOrRecompute: getOrRecomputeMock, + } as unknown as WorkspaceCacheService, + { + validateBuildAndRunWorkspaceMigration: + validateBuildAndRunWorkspaceMigrationMock, + } as unknown as WorkspaceMigrationValidateBuildAndRunService, + ); + }); + + const runOnWorkspace = (dryRun = false) => + command.runOnWorkspace({ + workspaceId: WORKSPACE_ID, + options: { dryRun }, + index: 0, + total: 1, + }); + + const mockWorkspaceCache = ({ + existingFields = [], + existingViews = [], + existingViewFields = [], + }: { + existingFields?: string[]; + existingViews?: string[]; + existingViewFields?: string[]; + }) => { + getOrRecomputeMock.mockResolvedValue({ + flatObjectMetadataMaps: buildByUniversalIdentifierMap([ + CAMPAIGN.universalIdentifier, + ]), + flatFieldMetadataMaps: buildByUniversalIdentifierMap(existingFields), + flatViewMaps: buildByUniversalIdentifierMap(existingViews), + flatViewFieldMaps: buildByUniversalIdentifierMap(existingViewFields), + }); + }; + + it('creates missing stat fields, the campaign view, and all campaign view fields', async () => { + mockWorkspaceCache({}); + + await runOnWorkspace(); + + expect(validateBuildAndRunWorkspaceMigrationMock).toHaveBeenCalledWith({ + isSystemBuild: true, + workspaceId: WORKSPACE_ID, + applicationUniversalIdentifier: STANDARD_APPLICATION.universalIdentifier, + allFlatEntityOperationByMetadataName: { + fieldMetadata: { + flatEntityToCreate: expect.arrayContaining( + STAT_FIELD_UNIVERSAL_IDENTIFIERS.map((universalIdentifier) => + expect.objectContaining({ universalIdentifier }), + ), + ), + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + view: { + flatEntityToCreate: [ + expect.objectContaining({ + universalIdentifier: CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER, + }), + ], + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + viewField: { + flatEntityToCreate: expect.arrayContaining( + CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.map( + (universalIdentifier) => + expect.objectContaining({ universalIdentifier }), + ), + ), + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + }); + expect( + validateBuildAndRunWorkspaceMigrationMock.mock.calls[0][0] + .allFlatEntityOperationByMetadataName.fieldMetadata.flatEntityToCreate, + ).toHaveLength(STAT_FIELD_UNIVERSAL_IDENTIFIERS.length); + expect( + validateBuildAndRunWorkspaceMigrationMock.mock.calls[0][0] + .allFlatEntityOperationByMetadataName.viewField.flatEntityToCreate, + ).toHaveLength(CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.length); + }); + + it('creates only missing pieces when rerun against a partially migrated workspace', async () => { + mockWorkspaceCache({ + existingFields: [CAMPAIGN.fields.sentCount.universalIdentifier], + existingViews: [CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER], + existingViewFields: [ + CAMPAIGN.views.allMessageCampaigns.viewFields.subject + .universalIdentifier, + ], + }); + + await runOnWorkspace(); + + const payload = + validateBuildAndRunWorkspaceMigrationMock.mock.calls[0][0] + .allFlatEntityOperationByMetadataName; + + expect(payload.fieldMetadata.flatEntityToCreate).toEqual( + expect.arrayContaining( + STAT_FIELD_UNIVERSAL_IDENTIFIERS.slice(1).map((universalIdentifier) => + expect.objectContaining({ universalIdentifier }), + ), + ), + ); + expect(payload.fieldMetadata.flatEntityToCreate).toHaveLength( + STAT_FIELD_UNIVERSAL_IDENTIFIERS.length - 1, + ); + expect(payload.view.flatEntityToCreate).toEqual([]); + expect(payload.viewField.flatEntityToCreate).toHaveLength( + CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.length - 1, + ); + }); + + it('skips the metadata migration when everything already exists', async () => { + mockWorkspaceCache({ + existingFields: STAT_FIELD_UNIVERSAL_IDENTIFIERS, + existingViews: [CAMPAIGN_VIEW_UNIVERSAL_IDENTIFIER], + existingViewFields: CAMPAIGN_VIEW_FIELD_UNIVERSAL_IDENTIFIERS, + }); + + await runOnWorkspace(); + + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); + + it('does not write metadata in dry-run mode', async () => { + mockWorkspaceCache({}); + + await runOnWorkspace(true); + + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); + + it('skips workspaces where the messageCampaign object is absent', async () => { + getOrRecomputeMock.mockResolvedValue({ + flatObjectMetadataMaps: buildByUniversalIdentifierMap([]), + flatFieldMetadataMaps: buildByUniversalIdentifierMap([]), + flatViewMaps: buildByUniversalIdentifierMap([]), + flatViewFieldMaps: buildByUniversalIdentifierMap([]), + }); + + await runOnWorkspace(); + + expect(findApplicationMock).not.toHaveBeenCalled(); + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423001-create-message-list-view.command.spec.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423001-create-message-list-view.command.spec.ts new file mode 100644 index 0000000000..4edb89dc8f --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/2-20-workspace-command-1783083423001-create-message-list-view.command.spec.ts @@ -0,0 +1,202 @@ +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; + +import { type WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { CreateMessageListViewCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-workspace-command-1783083423001-create-message-list-view.command'; +import { type ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { type WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { type WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +jest.mock( + 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant', +); + +const computeTwentyStandardApplicationAllFlatEntityMapsMock = + computeTwentyStandardApplicationAllFlatEntityMaps as jest.Mock; + +const WORKSPACE_ID = '20202020-0000-0000-0000-000000000001'; +const STANDARD_APPLICATION = { + id: '20202020-0000-0000-0000-0000000000aa', + universalIdentifier: '20202020-0000-0000-0000-0000000000bb', +}; + +const LIST = STANDARD_OBJECTS.messageList; +const LIST_VIEW_UNIVERSAL_IDENTIFIER = + LIST.views.allMessageLists.universalIdentifier; +const LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS = Object.values( + LIST.views.allMessageLists.viewFields, +).map((viewField) => viewField.universalIdentifier); + +const buildByUniversalIdentifierMap = (universalIdentifiers: string[]) => ({ + byUniversalIdentifier: Object.fromEntries( + universalIdentifiers.map((universalIdentifier) => [ + universalIdentifier, + { universalIdentifier }, + ]), + ), +}); + +describe('CreateMessageListViewCommand', () => { + let command: CreateMessageListViewCommand; + let findApplicationMock: jest.Mock; + let getOrRecomputeMock: jest.Mock; + let validateBuildAndRunWorkspaceMigrationMock: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + + findApplicationMock = jest.fn().mockResolvedValue({ + twentyStandardFlatApplication: STANDARD_APPLICATION, + }); + getOrRecomputeMock = jest.fn(); + validateBuildAndRunWorkspaceMigrationMock = jest + .fn() + .mockResolvedValue({ status: 'success' }); + + computeTwentyStandardApplicationAllFlatEntityMapsMock.mockReturnValue({ + allFlatEntityMaps: { + flatViewMaps: buildByUniversalIdentifierMap([ + LIST_VIEW_UNIVERSAL_IDENTIFIER, + ]), + flatViewFieldMaps: buildByUniversalIdentifierMap( + LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS, + ), + }, + }); + + command = new CreateMessageListViewCommand( + {} as WorkspaceIteratorService, + { + findWorkspaceTwentyStandardAndCustomApplicationOrThrow: + findApplicationMock, + } as unknown as ApplicationService, + { + getOrRecompute: getOrRecomputeMock, + } as unknown as WorkspaceCacheService, + { + validateBuildAndRunWorkspaceMigration: + validateBuildAndRunWorkspaceMigrationMock, + } as unknown as WorkspaceMigrationValidateBuildAndRunService, + ); + }); + + const runOnWorkspace = (dryRun = false) => + command.runOnWorkspace({ + workspaceId: WORKSPACE_ID, + options: { dryRun }, + index: 0, + total: 1, + }); + + const mockWorkspaceCache = ({ + existingViews = [], + existingViewFields = [], + }: { + existingViews?: string[]; + existingViewFields?: string[]; + }) => { + getOrRecomputeMock.mockResolvedValue({ + flatObjectMetadataMaps: buildByUniversalIdentifierMap([ + LIST.universalIdentifier, + ]), + flatViewMaps: buildByUniversalIdentifierMap(existingViews), + flatViewFieldMaps: buildByUniversalIdentifierMap(existingViewFields), + }); + }; + + it('creates the message list view and all view fields when missing', async () => { + mockWorkspaceCache({}); + + await runOnWorkspace(); + + expect(validateBuildAndRunWorkspaceMigrationMock).toHaveBeenCalledWith({ + isSystemBuild: true, + workspaceId: WORKSPACE_ID, + applicationUniversalIdentifier: STANDARD_APPLICATION.universalIdentifier, + allFlatEntityOperationByMetadataName: { + view: { + flatEntityToCreate: [ + expect.objectContaining({ + universalIdentifier: LIST_VIEW_UNIVERSAL_IDENTIFIER, + }), + ], + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + viewField: { + flatEntityToCreate: expect.arrayContaining( + LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.map((universalIdentifier) => + expect.objectContaining({ universalIdentifier }), + ), + ), + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + }); + expect( + validateBuildAndRunWorkspaceMigrationMock.mock.calls[0][0] + .allFlatEntityOperationByMetadataName.viewField.flatEntityToCreate, + ).toHaveLength(LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.length); + }); + + it('creates missing view fields when the view already exists', async () => { + mockWorkspaceCache({ + existingViews: [LIST_VIEW_UNIVERSAL_IDENTIFIER], + existingViewFields: [ + LIST.views.allMessageLists.viewFields.name.universalIdentifier, + ], + }); + + await runOnWorkspace(); + + const payload = + validateBuildAndRunWorkspaceMigrationMock.mock.calls[0][0] + .allFlatEntityOperationByMetadataName; + + expect(payload.view.flatEntityToCreate).toEqual([]); + expect(payload.viewField.flatEntityToCreate).toEqual( + expect.arrayContaining( + LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.slice(1).map( + (universalIdentifier) => + expect.objectContaining({ universalIdentifier }), + ), + ), + ); + expect(payload.viewField.flatEntityToCreate).toHaveLength( + LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS.length - 1, + ); + }); + + it('does nothing when the view and view fields already exist', async () => { + mockWorkspaceCache({ + existingViews: [LIST_VIEW_UNIVERSAL_IDENTIFIER], + existingViewFields: LIST_VIEW_FIELD_UNIVERSAL_IDENTIFIERS, + }); + + await runOnWorkspace(); + + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); + + it('does not write metadata in dry-run mode', async () => { + mockWorkspaceCache({}); + + await runOnWorkspace(true); + + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); + + it('skips workspaces where the messageList object is absent', async () => { + getOrRecomputeMock.mockResolvedValue({ + flatObjectMetadataMaps: buildByUniversalIdentifierMap([]), + flatViewMaps: buildByUniversalIdentifierMap([]), + flatViewFieldMaps: buildByUniversalIdentifierMap([]), + }); + + await runOnWorkspace(); + + expect(findApplicationMock).not.toHaveBeenCalled(); + expect(validateBuildAndRunWorkspaceMigrationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/drop-metadata-standard-overrides-column.instance-command.spec.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/drop-metadata-standard-overrides-column.instance-command.spec.ts index 535f242758..3f7638adf2 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/drop-metadata-standard-overrides-column.instance-command.spec.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/__tests__/drop-metadata-standard-overrides-column.instance-command.spec.ts @@ -1,6 +1,6 @@ import { type QueryRunner } from 'typeorm'; -import { DropMetadataStandardOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column'; +import { DropMetadataStandardOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column'; import { DROP_METADATA_STANDARD_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-20/drop-metadata-standard-overrides-column-upgrade-command-name.constant'; import { getRegisteredInstanceCommandMetadata } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator'; @@ -19,7 +19,7 @@ describe('DropMetadataStandardOverridesColumnFastInstanceCommand', () => { expect(metadata).toEqual({ version: '2.20.0', - timestamp: 1825000000000, + timestamp: 1782991100000, type: 'fast', }); }); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/drop-metadata-standard-overrides-column-upgrade-command-name.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/drop-metadata-standard-overrides-column-upgrade-command-name.constant.ts index d85d376080..1366e6bdf5 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/drop-metadata-standard-overrides-column-upgrade-command-name.constant.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/drop-metadata-standard-overrides-column-upgrade-command-name.constant.ts @@ -1,4 +1,4 @@ // Registered name of the 2.20 drop command. Wire to @WasRemovedInUpgrade on the // standardOverrides columns once 2.20 is current. export const DROP_METADATA_STANDARD_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME = - '2.20.0_DropMetadataStandardOverridesColumnFastInstanceCommand_1825000000000'; + '2.20.0_DropMetadataStandardOverridesColumnFastInstanceCommand_1782991100000'; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts index cc5d934d7b..bc5cf04a4b 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts @@ -93,7 +93,7 @@ import { AddPendingQuestionMessageIdToAgentChatThreadFastInstanceCommand } from import { AddWorkspaceDiscoverabilityToWorkspaceFastInstanceCommand } from './2-19/2-19-instance-command-fast-1783004140000-add-workspace-discoverability-to-workspace'; import { AddStatusToFileFastInstanceCommand } from './2-19/2-19-instance-command-fast-1783082964705-add-status-to-file'; import { AddPendingMimeCheckToFileFastInstanceCommand } from './2-19/2-19-instance-command-fast-1783094691548-add-pending-mime-check-to-file'; -import { DropMetadataStandardOverridesColumnFastInstanceCommand } from './2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column'; +import { DropMetadataStandardOverridesColumnFastInstanceCommand } from './2-20/2-20-instance-command-fast-1782991100000-drop-metadata-standard-overrides-column'; import { AddLogoToApplicationRegistrationFastInstanceCommand } from './2-19/2-19-instance-command-fast-1783069672191-add-logo-to-application-registration'; import { BackfillLogoOnApplicationRegistrationSlowInstanceCommand } from './2-19/2-19-instance-command-slow-1783069673191-backfill-logo-on-application-registration'; import { AddDisplayFieldsToApplicationRegistrationFastInstanceCommand } from './2-19/2-19-instance-command-fast-1783073776590-add-display-fields-to-application-registration'; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/workspace-command-provider.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/workspace-command-provider.module.ts index 3e6f0c0066..87e10edd2e 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/workspace-command-provider.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/workspace-command-provider.module.ts @@ -20,6 +20,7 @@ import { V2_16_UpgradeVersionCommandModule } from 'src/database/commands/upgrade import { V2_17_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-17/2-17-upgrade-version-command.module'; import { V2_18_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-18/2-18-upgrade-version-command.module'; import { V2_19_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-19/2-19-upgrade-version-command.module'; +import { V2_20_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-20/2-20-upgrade-version-command.module'; @Module({ imports: [ @@ -43,6 +44,7 @@ import { V2_19_UpgradeVersionCommandModule } from 'src/database/commands/upgrade V2_17_UpgradeVersionCommandModule, V2_18_UpgradeVersionCommandModule, V2_19_UpgradeVersionCommandModule, + V2_20_UpgradeVersionCommandModule, ], }) export class WorkspaceCommandProviderModule {} diff --git a/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts b/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts index 390277fccb..ee9575f141 100644 --- a/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts @@ -1,5 +1,6 @@ export enum CacheStorageNamespace { ModuleMessaging = 'module:messaging', + ModuleEmailing = 'module:emailing', ModuleCalendar = 'module:calendar', ModuleWorkflow = 'module:workflow', EngineWorkspace = 'engine:workspace', diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/constants/campaign.constant.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/constants/campaign.constant.ts index 5b14bb915c..d9239f74ba 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/constants/campaign.constant.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/constants/campaign.constant.ts @@ -17,6 +17,11 @@ export const CAMPAIGN_STATUS = { export const MATERIALIZE_CAMPAIGN_JOB = 'MaterializeCampaignJob'; export const SEND_CAMPAIGN_EMAIL_JOB = 'SendCampaignEmailJob'; +export const REFRESH_CAMPAIGN_STATS_JOB = 'RefreshCampaignStatsJob'; + +export const CAMPAIGN_STATS_REFRESH_DEBOUNCE_MS = 10_000; +export const CAMPAIGN_STATS_REFRESH_DELAY_MS = + CAMPAIGN_STATS_REFRESH_DEBOUNCE_MS + 2_000; export const MAX_CAMPAIGN_RECIPIENTS = 10000; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type.ts new file mode 100644 index 0000000000..c9fcc34752 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type.ts @@ -0,0 +1,4 @@ +export type RefreshCampaignStatsJobData = { + workspaceId: string; + campaignId: string; +}; diff --git a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts index e9794ce912..53cb76391b 100644 --- a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts +++ b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts @@ -22,6 +22,7 @@ import { EmailSenderJob } from 'src/engine/core-modules/email/email-sender.job'; import { EmailModule } from 'src/engine/core-modules/email/email.module'; import { EmailingModule } from 'src/modules/emailing/emailing.module'; import { MaterializeCampaignJob } from 'src/modules/emailing/jobs/materialize-campaign.job'; +import { RefreshCampaignStatsJob } from 'src/modules/emailing/jobs/refresh-campaign-stats.job'; import { SendCampaignEmailJob } from 'src/modules/emailing/jobs/send-campaign-email.job'; import { EnterpriseModule } from 'src/engine/core-modules/enterprise/enterprise.module'; import { EventLogIngestionModule } from 'src/engine/core-modules/event-logs/ingest/event-log-ingestion.module'; @@ -104,6 +105,7 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module'; EmailSenderJob, SendCampaignEmailJob, MaterializeCampaignJob, + RefreshCampaignStatsJob, UpdateSubscriptionQuantityJob, HandleWorkspaceMemberDeletedJob, CleanWorkspaceDeletionWarningUserVarsJob, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap index ccfc8c1206..4d4b038d93 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap @@ -906,86 +906,86 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "message": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000418", + "id": "00000000-0000-0000-0000-000000000439", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000421", + "id": "00000000-0000-0000-0000-000000000442", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000420", + "id": "00000000-0000-0000-0000-000000000441", }, "deliveryStatus": { - "id": "00000000-0000-0000-0000-000000000433", + "id": "00000000-0000-0000-0000-000000000454", }, "headerMessageId": { - "id": "00000000-0000-0000-0000-000000000425", + "id": "00000000-0000-0000-0000-000000000446", }, "id": { - "id": "00000000-0000-0000-0000-000000000417", + "id": "00000000-0000-0000-0000-000000000438", }, "isDraft": { - "id": "00000000-0000-0000-0000-000000000434", + "id": "00000000-0000-0000-0000-000000000455", }, "messageCampaign": { - "id": "00000000-0000-0000-0000-000000000432", + "id": "00000000-0000-0000-0000-000000000453", }, "messageChannelMessageAssociations": { - "id": "00000000-0000-0000-0000-000000000431", + "id": "00000000-0000-0000-0000-000000000452", }, "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000430", + "id": "00000000-0000-0000-0000-000000000451", }, "messageThread": { - "id": "00000000-0000-0000-0000-000000000426", + "id": "00000000-0000-0000-0000-000000000447", }, "position": { - "id": "00000000-0000-0000-0000-000000000423", + "id": "00000000-0000-0000-0000-000000000444", }, "receivedAt": { - "id": "00000000-0000-0000-0000-000000000429", + "id": "00000000-0000-0000-0000-000000000450", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000424", + "id": "00000000-0000-0000-0000-000000000445", }, "subject": { - "id": "00000000-0000-0000-0000-000000000427", + "id": "00000000-0000-0000-0000-000000000448", }, "text": { - "id": "00000000-0000-0000-0000-000000000428", + "id": "00000000-0000-0000-0000-000000000449", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000419", + "id": "00000000-0000-0000-0000-000000000440", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000422", + "id": "00000000-0000-0000-0000-000000000443", }, }, - "id": "00000000-0000-0000-0000-000000000443", + "id": "00000000-0000-0000-0000-000000000464", "views": { "allMessages": { - "id": "00000000-0000-0000-0000-000000000442", + "id": "00000000-0000-0000-0000-000000000463", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000441", + "id": "00000000-0000-0000-0000-000000000462", }, "headerMessageId": { - "id": "00000000-0000-0000-0000-000000000439", + "id": "00000000-0000-0000-0000-000000000460", }, "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000437", + "id": "00000000-0000-0000-0000-000000000458", }, "messageThread": { - "id": "00000000-0000-0000-0000-000000000436", + "id": "00000000-0000-0000-0000-000000000457", }, "receivedAt": { - "id": "00000000-0000-0000-0000-000000000438", + "id": "00000000-0000-0000-0000-000000000459", }, "subject": { - "id": "00000000-0000-0000-0000-000000000435", + "id": "00000000-0000-0000-0000-000000000456", }, "text": { - "id": "00000000-0000-0000-0000-000000000440", + "id": "00000000-0000-0000-0000-000000000461", }, }, "viewGroups": {}, @@ -997,6 +997,12 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "bodyTemplate": { "id": "00000000-0000-0000-0000-000000000279", }, + "bouncedCount": { + "id": "00000000-0000-0000-0000-000000000285", + }, + "complainedCount": { + "id": "00000000-0000-0000-0000-000000000286", + }, "createdAt": { "id": "00000000-0000-0000-0000-000000000271", }, @@ -1006,6 +1012,9 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "deletedAt": { "id": "00000000-0000-0000-0000-000000000273", }, + "failedCount": { + "id": "00000000-0000-0000-0000-000000000284", + }, "fromAddress": { "id": "00000000-0000-0000-0000-000000000280", }, @@ -1013,16 +1022,16 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000270", }, "list": { - "id": "00000000-0000-0000-0000-000000000284", + "id": "00000000-0000-0000-0000-000000000288", }, "messages": { - "id": "00000000-0000-0000-0000-000000000286", + "id": "00000000-0000-0000-0000-000000000290", }, "position": { "id": "00000000-0000-0000-0000-000000000276", }, "recipients": { - "id": "00000000-0000-0000-0000-000000000287", + "id": "00000000-0000-0000-0000-000000000291", }, "searchVector": { "id": "00000000-0000-0000-0000-000000000277", @@ -1030,6 +1039,9 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "sentAt": { "id": "00000000-0000-0000-0000-000000000282", }, + "sentCount": { + "id": "00000000-0000-0000-0000-000000000283", + }, "status": { "id": "00000000-0000-0000-0000-000000000281", }, @@ -1037,10 +1049,10 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000278", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000285", + "id": "00000000-0000-0000-0000-000000000289", }, "unsubscribeTopicId": { - "id": "00000000-0000-0000-0000-000000000283", + "id": "00000000-0000-0000-0000-000000000287", }, "updatedAt": { "id": "00000000-0000-0000-0000-000000000272", @@ -1049,109 +1061,150 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000275", }, }, - "id": "00000000-0000-0000-0000-000000000288", - "views": undefined, + "id": "00000000-0000-0000-0000-000000000304", + "views": { + "allMessageCampaigns": { + "id": "00000000-0000-0000-0000-000000000303", + "viewFieldGroups": {}, + "viewFields": { + "bouncedCount": { + "id": "00000000-0000-0000-0000-000000000299", + }, + "complainedCount": { + "id": "00000000-0000-0000-0000-000000000300", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000302", + }, + "failedCount": { + "id": "00000000-0000-0000-0000-000000000298", + }, + "fromAddress": { + "id": "00000000-0000-0000-0000-000000000295", + }, + "list": { + "id": "00000000-0000-0000-0000-000000000294", + }, + "recipients": { + "id": "00000000-0000-0000-0000-000000000301", + }, + "sentAt": { + "id": "00000000-0000-0000-0000-000000000296", + }, + "sentCount": { + "id": "00000000-0000-0000-0000-000000000297", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000293", + }, + "subject": { + "id": "00000000-0000-0000-0000-000000000292", + }, + }, + "viewGroups": {}, + }, + }, }, "messageChannelMessageAssociation": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000314", + "id": "00000000-0000-0000-0000-000000000335", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000317", + "id": "00000000-0000-0000-0000-000000000338", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000316", + "id": "00000000-0000-0000-0000-000000000337", }, "direction": { - "id": "00000000-0000-0000-0000-000000000326", + "id": "00000000-0000-0000-0000-000000000347", }, "id": { - "id": "00000000-0000-0000-0000-000000000313", + "id": "00000000-0000-0000-0000-000000000334", }, "message": { - "id": "00000000-0000-0000-0000-000000000322", + "id": "00000000-0000-0000-0000-000000000343", }, "messageChannelId": { - "id": "00000000-0000-0000-0000-000000000321", + "id": "00000000-0000-0000-0000-000000000342", }, "messageExternalId": { - "id": "00000000-0000-0000-0000-000000000323", + "id": "00000000-0000-0000-0000-000000000344", }, "messageFolders": { - "id": "00000000-0000-0000-0000-000000000327", + "id": "00000000-0000-0000-0000-000000000348", }, "messageThread": { - "id": "00000000-0000-0000-0000-000000000324", + "id": "00000000-0000-0000-0000-000000000345", }, "messageThreadExternalId": { - "id": "00000000-0000-0000-0000-000000000325", + "id": "00000000-0000-0000-0000-000000000346", }, "position": { - "id": "00000000-0000-0000-0000-000000000319", + "id": "00000000-0000-0000-0000-000000000340", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000320", + "id": "00000000-0000-0000-0000-000000000341", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000315", + "id": "00000000-0000-0000-0000-000000000336", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000318", + "id": "00000000-0000-0000-0000-000000000339", }, }, - "id": "00000000-0000-0000-0000-000000000343", + "id": "00000000-0000-0000-0000-000000000364", "views": { "allMessageChannelMessageAssociations": { - "id": "00000000-0000-0000-0000-000000000333", + "id": "00000000-0000-0000-0000-000000000354", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000332", + "id": "00000000-0000-0000-0000-000000000353", }, "direction": { - "id": "00000000-0000-0000-0000-000000000331", + "id": "00000000-0000-0000-0000-000000000352", }, "message": { - "id": "00000000-0000-0000-0000-000000000329", + "id": "00000000-0000-0000-0000-000000000350", }, "messageChannelId": { - "id": "00000000-0000-0000-0000-000000000328", + "id": "00000000-0000-0000-0000-000000000349", }, "messageExternalId": { - "id": "00000000-0000-0000-0000-000000000330", + "id": "00000000-0000-0000-0000-000000000351", }, }, "viewGroups": {}, }, "messageChannelMessageAssociationRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000342", + "id": "00000000-0000-0000-0000-000000000363", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000340", + "id": "00000000-0000-0000-0000-000000000361", }, "system": { - "id": "00000000-0000-0000-0000-000000000341", + "id": "00000000-0000-0000-0000-000000000362", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000338", + "id": "00000000-0000-0000-0000-000000000359", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000339", + "id": "00000000-0000-0000-0000-000000000360", }, "direction": { - "id": "00000000-0000-0000-0000-000000000337", + "id": "00000000-0000-0000-0000-000000000358", }, "message": { - "id": "00000000-0000-0000-0000-000000000335", + "id": "00000000-0000-0000-0000-000000000356", }, "messageChannelId": { - "id": "00000000-0000-0000-0000-000000000334", + "id": "00000000-0000-0000-0000-000000000355", }, "messageExternalId": { - "id": "00000000-0000-0000-0000-000000000336", + "id": "00000000-0000-0000-0000-000000000357", }, }, "viewGroups": {}, @@ -1161,76 +1214,76 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "messageChannelMessageAssociationMessageFolder": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000345", + "id": "00000000-0000-0000-0000-000000000366", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000348", + "id": "00000000-0000-0000-0000-000000000369", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000347", + "id": "00000000-0000-0000-0000-000000000368", }, "id": { - "id": "00000000-0000-0000-0000-000000000344", + "id": "00000000-0000-0000-0000-000000000365", }, "messageChannelMessageAssociation": { - "id": "00000000-0000-0000-0000-000000000352", + "id": "00000000-0000-0000-0000-000000000373", }, "messageFolderId": { - "id": "00000000-0000-0000-0000-000000000353", + "id": "00000000-0000-0000-0000-000000000374", }, "position": { - "id": "00000000-0000-0000-0000-000000000350", + "id": "00000000-0000-0000-0000-000000000371", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000351", + "id": "00000000-0000-0000-0000-000000000372", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000346", + "id": "00000000-0000-0000-0000-000000000367", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000349", + "id": "00000000-0000-0000-0000-000000000370", }, }, - "id": "00000000-0000-0000-0000-000000000365", + "id": "00000000-0000-0000-0000-000000000386", "views": { "allMessageChannelMessageAssociationMessageFolders": { - "id": "00000000-0000-0000-0000-000000000357", + "id": "00000000-0000-0000-0000-000000000378", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000356", + "id": "00000000-0000-0000-0000-000000000377", }, "messageChannelMessageAssociation": { - "id": "00000000-0000-0000-0000-000000000354", + "id": "00000000-0000-0000-0000-000000000375", }, "messageFolderId": { - "id": "00000000-0000-0000-0000-000000000355", + "id": "00000000-0000-0000-0000-000000000376", }, }, "viewGroups": {}, }, "messageChannelMessageAssociationMessageFolderRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000364", + "id": "00000000-0000-0000-0000-000000000385", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000362", + "id": "00000000-0000-0000-0000-000000000383", }, "system": { - "id": "00000000-0000-0000-0000-000000000363", + "id": "00000000-0000-0000-0000-000000000384", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000360", + "id": "00000000-0000-0000-0000-000000000381", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000361", + "id": "00000000-0000-0000-0000-000000000382", }, "messageChannelMessageAssociation": { - "id": "00000000-0000-0000-0000-000000000358", + "id": "00000000-0000-0000-0000-000000000379", }, "messageFolderId": { - "id": "00000000-0000-0000-0000-000000000359", + "id": "00000000-0000-0000-0000-000000000380", }, }, "viewGroups": {}, @@ -1240,190 +1293,210 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "messageList": { "fields": { "campaigns": { - "id": "00000000-0000-0000-0000-000000000299", + "id": "00000000-0000-0000-0000-000000000315", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000290", + "id": "00000000-0000-0000-0000-000000000306", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000293", + "id": "00000000-0000-0000-0000-000000000309", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000292", + "id": "00000000-0000-0000-0000-000000000308", }, "id": { - "id": "00000000-0000-0000-0000-000000000289", + "id": "00000000-0000-0000-0000-000000000305", }, "members": { - "id": "00000000-0000-0000-0000-000000000298", + "id": "00000000-0000-0000-0000-000000000314", }, "name": { - "id": "00000000-0000-0000-0000-000000000297", + "id": "00000000-0000-0000-0000-000000000313", }, "position": { - "id": "00000000-0000-0000-0000-000000000295", + "id": "00000000-0000-0000-0000-000000000311", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000296", + "id": "00000000-0000-0000-0000-000000000312", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000300", + "id": "00000000-0000-0000-0000-000000000316", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000291", + "id": "00000000-0000-0000-0000-000000000307", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000294", + "id": "00000000-0000-0000-0000-000000000310", + }, + }, + "id": "00000000-0000-0000-0000-000000000322", + "views": { + "allMessageLists": { + "id": "00000000-0000-0000-0000-000000000321", + "viewFieldGroups": {}, + "viewFields": { + "campaigns": { + "id": "00000000-0000-0000-0000-000000000319", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000320", + }, + "members": { + "id": "00000000-0000-0000-0000-000000000318", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000317", + }, + }, + "viewGroups": {}, }, }, - "id": "00000000-0000-0000-0000-000000000301", - "views": undefined, }, "messageListMember": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000303", + "id": "00000000-0000-0000-0000-000000000324", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000306", + "id": "00000000-0000-0000-0000-000000000327", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000305", + "id": "00000000-0000-0000-0000-000000000326", }, "id": { - "id": "00000000-0000-0000-0000-000000000302", + "id": "00000000-0000-0000-0000-000000000323", }, "list": { - "id": "00000000-0000-0000-0000-000000000311", + "id": "00000000-0000-0000-0000-000000000332", }, "person": { - "id": "00000000-0000-0000-0000-000000000310", + "id": "00000000-0000-0000-0000-000000000331", }, "position": { - "id": "00000000-0000-0000-0000-000000000308", + "id": "00000000-0000-0000-0000-000000000329", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000309", + "id": "00000000-0000-0000-0000-000000000330", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000304", + "id": "00000000-0000-0000-0000-000000000325", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000307", + "id": "00000000-0000-0000-0000-000000000328", }, }, - "id": "00000000-0000-0000-0000-000000000312", + "id": "00000000-0000-0000-0000-000000000333", "views": undefined, }, "messageParticipant": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000367", + "id": "00000000-0000-0000-0000-000000000388", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000370", + "id": "00000000-0000-0000-0000-000000000391", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000369", + "id": "00000000-0000-0000-0000-000000000390", }, "displayName": { - "id": "00000000-0000-0000-0000-000000000377", + "id": "00000000-0000-0000-0000-000000000398", }, "handle": { - "id": "00000000-0000-0000-0000-000000000376", + "id": "00000000-0000-0000-0000-000000000397", }, "id": { - "id": "00000000-0000-0000-0000-000000000366", + "id": "00000000-0000-0000-0000-000000000387", }, "message": { - "id": "00000000-0000-0000-0000-000000000374", + "id": "00000000-0000-0000-0000-000000000395", }, "messageCampaign": { - "id": "00000000-0000-0000-0000-000000000380", + "id": "00000000-0000-0000-0000-000000000401", }, "person": { - "id": "00000000-0000-0000-0000-000000000378", + "id": "00000000-0000-0000-0000-000000000399", }, "position": { - "id": "00000000-0000-0000-0000-000000000372", + "id": "00000000-0000-0000-0000-000000000393", }, "role": { - "id": "00000000-0000-0000-0000-000000000375", + "id": "00000000-0000-0000-0000-000000000396", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000373", + "id": "00000000-0000-0000-0000-000000000394", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000368", + "id": "00000000-0000-0000-0000-000000000389", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000371", + "id": "00000000-0000-0000-0000-000000000392", }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000379", + "id": "00000000-0000-0000-0000-000000000400", }, }, - "id": "00000000-0000-0000-0000-000000000399", + "id": "00000000-0000-0000-0000-000000000420", "views": { "allMessageParticipants": { - "id": "00000000-0000-0000-0000-000000000388", + "id": "00000000-0000-0000-0000-000000000409", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000387", + "id": "00000000-0000-0000-0000-000000000408", }, "displayName": { - "id": "00000000-0000-0000-0000-000000000384", + "id": "00000000-0000-0000-0000-000000000405", }, "handle": { - "id": "00000000-0000-0000-0000-000000000383", + "id": "00000000-0000-0000-0000-000000000404", }, "message": { - "id": "00000000-0000-0000-0000-000000000381", + "id": "00000000-0000-0000-0000-000000000402", }, "person": { - "id": "00000000-0000-0000-0000-000000000385", + "id": "00000000-0000-0000-0000-000000000406", }, "role": { - "id": "00000000-0000-0000-0000-000000000382", + "id": "00000000-0000-0000-0000-000000000403", }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000386", + "id": "00000000-0000-0000-0000-000000000407", }, }, "viewGroups": {}, }, "messageParticipantRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000398", + "id": "00000000-0000-0000-0000-000000000419", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000396", + "id": "00000000-0000-0000-0000-000000000417", }, "system": { - "id": "00000000-0000-0000-0000-000000000397", + "id": "00000000-0000-0000-0000-000000000418", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000394", + "id": "00000000-0000-0000-0000-000000000415", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000395", + "id": "00000000-0000-0000-0000-000000000416", }, "displayName": { - "id": "00000000-0000-0000-0000-000000000391", + "id": "00000000-0000-0000-0000-000000000412", }, "message": { - "id": "00000000-0000-0000-0000-000000000389", + "id": "00000000-0000-0000-0000-000000000410", }, "person": { - "id": "00000000-0000-0000-0000-000000000392", + "id": "00000000-0000-0000-0000-000000000413", }, "role": { - "id": "00000000-0000-0000-0000-000000000390", + "id": "00000000-0000-0000-0000-000000000411", }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000393", + "id": "00000000-0000-0000-0000-000000000414", }, }, "viewGroups": {}, @@ -1433,56 +1506,56 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "messageThread": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000401", + "id": "00000000-0000-0000-0000-000000000422", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000404", + "id": "00000000-0000-0000-0000-000000000425", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000403", + "id": "00000000-0000-0000-0000-000000000424", }, "id": { - "id": "00000000-0000-0000-0000-000000000400", + "id": "00000000-0000-0000-0000-000000000421", }, "messageChannelMessageAssociations": { - "id": "00000000-0000-0000-0000-000000000409", + "id": "00000000-0000-0000-0000-000000000430", }, "messages": { - "id": "00000000-0000-0000-0000-000000000408", + "id": "00000000-0000-0000-0000-000000000429", }, "position": { - "id": "00000000-0000-0000-0000-000000000406", + "id": "00000000-0000-0000-0000-000000000427", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000407", + "id": "00000000-0000-0000-0000-000000000428", }, "subject": { - "id": "00000000-0000-0000-0000-000000000410", + "id": "00000000-0000-0000-0000-000000000431", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000402", + "id": "00000000-0000-0000-0000-000000000423", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000405", + "id": "00000000-0000-0000-0000-000000000426", }, }, - "id": "00000000-0000-0000-0000-000000000416", + "id": "00000000-0000-0000-0000-000000000437", "views": { "allMessageThreads": { - "id": "00000000-0000-0000-0000-000000000415", + "id": "00000000-0000-0000-0000-000000000436", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000414", + "id": "00000000-0000-0000-0000-000000000435", }, "messages": { - "id": "00000000-0000-0000-0000-000000000412", + "id": "00000000-0000-0000-0000-000000000433", }, "subject": { - "id": "00000000-0000-0000-0000-000000000411", + "id": "00000000-0000-0000-0000-000000000432", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000413", + "id": "00000000-0000-0000-0000-000000000434", }, }, "viewGroups": {}, @@ -1492,103 +1565,103 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "note": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000455", + "id": "00000000-0000-0000-0000-000000000476", }, "bodyV2": { - "id": "00000000-0000-0000-0000-000000000453", + "id": "00000000-0000-0000-0000-000000000474", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000445", + "id": "00000000-0000-0000-0000-000000000466", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000448", + "id": "00000000-0000-0000-0000-000000000469", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000447", + "id": "00000000-0000-0000-0000-000000000468", }, "id": { - "id": "00000000-0000-0000-0000-000000000444", + "id": "00000000-0000-0000-0000-000000000465", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000454", + "id": "00000000-0000-0000-0000-000000000475", }, "position": { - "id": "00000000-0000-0000-0000-000000000450", + "id": "00000000-0000-0000-0000-000000000471", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000451", + "id": "00000000-0000-0000-0000-000000000472", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000456", + "id": "00000000-0000-0000-0000-000000000477", }, "title": { - "id": "00000000-0000-0000-0000-000000000452", + "id": "00000000-0000-0000-0000-000000000473", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000446", + "id": "00000000-0000-0000-0000-000000000467", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000449", + "id": "00000000-0000-0000-0000-000000000470", }, }, - "id": "00000000-0000-0000-0000-000000000474", + "id": "00000000-0000-0000-0000-000000000495", "views": { "allNotes": { - "id": "00000000-0000-0000-0000-000000000462", + "id": "00000000-0000-0000-0000-000000000483", "viewFieldGroups": {}, "viewFields": { "bodyV2": { - "id": "00000000-0000-0000-0000-000000000459", + "id": "00000000-0000-0000-0000-000000000480", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000461", + "id": "00000000-0000-0000-0000-000000000482", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000460", + "id": "00000000-0000-0000-0000-000000000481", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000458", + "id": "00000000-0000-0000-0000-000000000479", }, "title": { - "id": "00000000-0000-0000-0000-000000000457", + "id": "00000000-0000-0000-0000-000000000478", }, }, "viewGroups": {}, }, "noteRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000473", + "id": "00000000-0000-0000-0000-000000000494", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000471", + "id": "00000000-0000-0000-0000-000000000492", }, "system": { - "id": "00000000-0000-0000-0000-000000000472", + "id": "00000000-0000-0000-0000-000000000493", }, }, "viewFields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000469", + "id": "00000000-0000-0000-0000-000000000490", }, "bodyV2": { - "id": "00000000-0000-0000-0000-000000000466", + "id": "00000000-0000-0000-0000-000000000487", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000463", + "id": "00000000-0000-0000-0000-000000000484", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000464", + "id": "00000000-0000-0000-0000-000000000485", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000465", + "id": "00000000-0000-0000-0000-000000000486", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000470", + "id": "00000000-0000-0000-0000-000000000491", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000467", + "id": "00000000-0000-0000-0000-000000000488", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000468", + "id": "00000000-0000-0000-0000-000000000489", }, }, "viewGroups": {}, @@ -1598,62 +1671,62 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "noteTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000476", + "id": "00000000-0000-0000-0000-000000000497", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000479", + "id": "00000000-0000-0000-0000-000000000500", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000478", + "id": "00000000-0000-0000-0000-000000000499", }, "id": { - "id": "00000000-0000-0000-0000-000000000475", + "id": "00000000-0000-0000-0000-000000000496", }, "note": { - "id": "00000000-0000-0000-0000-000000000483", + "id": "00000000-0000-0000-0000-000000000504", }, "position": { - "id": "00000000-0000-0000-0000-000000000481", + "id": "00000000-0000-0000-0000-000000000502", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000482", + "id": "00000000-0000-0000-0000-000000000503", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000485", + "id": "00000000-0000-0000-0000-000000000506", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000486", + "id": "00000000-0000-0000-0000-000000000507", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000484", + "id": "00000000-0000-0000-0000-000000000505", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000477", + "id": "00000000-0000-0000-0000-000000000498", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000480", + "id": "00000000-0000-0000-0000-000000000501", }, }, - "id": "00000000-0000-0000-0000-000000000493", + "id": "00000000-0000-0000-0000-000000000514", "views": { "allNoteTargets": { - "id": "00000000-0000-0000-0000-000000000492", + "id": "00000000-0000-0000-0000-000000000513", "viewFieldGroups": {}, "viewFields": { "id": { - "id": "00000000-0000-0000-0000-000000000487", + "id": "00000000-0000-0000-0000-000000000508", }, "note": { - "id": "00000000-0000-0000-0000-000000000488", + "id": "00000000-0000-0000-0000-000000000509", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000490", + "id": "00000000-0000-0000-0000-000000000511", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000491", + "id": "00000000-0000-0000-0000-000000000512", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000489", + "id": "00000000-0000-0000-0000-000000000510", }, }, "viewGroups": {}, @@ -1663,186 +1736,186 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "opportunity": { "fields": { "amount": { - "id": "00000000-0000-0000-0000-000000000503", + "id": "00000000-0000-0000-0000-000000000524", }, "attachments": { - "id": "00000000-0000-0000-0000-000000000511", + "id": "00000000-0000-0000-0000-000000000532", }, "closeDate": { - "id": "00000000-0000-0000-0000-000000000504", + "id": "00000000-0000-0000-0000-000000000525", }, "company": { - "id": "00000000-0000-0000-0000-000000000507", + "id": "00000000-0000-0000-0000-000000000528", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000495", + "id": "00000000-0000-0000-0000-000000000516", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000498", + "id": "00000000-0000-0000-0000-000000000519", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000497", + "id": "00000000-0000-0000-0000-000000000518", }, "id": { - "id": "00000000-0000-0000-0000-000000000494", + "id": "00000000-0000-0000-0000-000000000515", }, "name": { - "id": "00000000-0000-0000-0000-000000000502", + "id": "00000000-0000-0000-0000-000000000523", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000510", + "id": "00000000-0000-0000-0000-000000000531", }, "owner": { - "id": "00000000-0000-0000-0000-000000000508", + "id": "00000000-0000-0000-0000-000000000529", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000506", + "id": "00000000-0000-0000-0000-000000000527", }, "position": { - "id": "00000000-0000-0000-0000-000000000500", + "id": "00000000-0000-0000-0000-000000000521", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000501", + "id": "00000000-0000-0000-0000-000000000522", }, "stage": { - "id": "00000000-0000-0000-0000-000000000505", + "id": "00000000-0000-0000-0000-000000000526", }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000509", + "id": "00000000-0000-0000-0000-000000000530", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000512", + "id": "00000000-0000-0000-0000-000000000533", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000496", + "id": "00000000-0000-0000-0000-000000000517", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000499", + "id": "00000000-0000-0000-0000-000000000520", }, }, - "id": "00000000-0000-0000-0000-000000000550", + "id": "00000000-0000-0000-0000-000000000571", "views": { "allOpportunities": { - "id": "00000000-0000-0000-0000-000000000519", + "id": "00000000-0000-0000-0000-000000000540", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000514", + "id": "00000000-0000-0000-0000-000000000535", }, "closeDate": { - "id": "00000000-0000-0000-0000-000000000516", + "id": "00000000-0000-0000-0000-000000000537", }, "company": { - "id": "00000000-0000-0000-0000-000000000517", + "id": "00000000-0000-0000-0000-000000000538", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000515", + "id": "00000000-0000-0000-0000-000000000536", }, "name": { - "id": "00000000-0000-0000-0000-000000000513", + "id": "00000000-0000-0000-0000-000000000534", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000518", + "id": "00000000-0000-0000-0000-000000000539", }, }, "viewGroups": {}, }, "byStage": { - "id": "00000000-0000-0000-0000-000000000531", + "id": "00000000-0000-0000-0000-000000000552", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000521", + "id": "00000000-0000-0000-0000-000000000542", }, "closeDate": { - "id": "00000000-0000-0000-0000-000000000523", + "id": "00000000-0000-0000-0000-000000000544", }, "company": { - "id": "00000000-0000-0000-0000-000000000524", + "id": "00000000-0000-0000-0000-000000000545", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000522", + "id": "00000000-0000-0000-0000-000000000543", }, "name": { - "id": "00000000-0000-0000-0000-000000000520", + "id": "00000000-0000-0000-0000-000000000541", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000525", + "id": "00000000-0000-0000-0000-000000000546", }, }, "viewGroups": { "customer": { - "id": "00000000-0000-0000-0000-000000000530", + "id": "00000000-0000-0000-0000-000000000551", }, "meeting": { - "id": "00000000-0000-0000-0000-000000000528", + "id": "00000000-0000-0000-0000-000000000549", }, "new": { - "id": "00000000-0000-0000-0000-000000000526", + "id": "00000000-0000-0000-0000-000000000547", }, "proposal": { - "id": "00000000-0000-0000-0000-000000000529", + "id": "00000000-0000-0000-0000-000000000550", }, "screening": { - "id": "00000000-0000-0000-0000-000000000527", + "id": "00000000-0000-0000-0000-000000000548", }, }, }, "opportunityRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000549", + "id": "00000000-0000-0000-0000-000000000570", "viewFieldGroups": { "deal": { - "id": "00000000-0000-0000-0000-000000000546", + "id": "00000000-0000-0000-0000-000000000567", }, "relations": { - "id": "00000000-0000-0000-0000-000000000547", + "id": "00000000-0000-0000-0000-000000000568", }, "system": { - "id": "00000000-0000-0000-0000-000000000548", + "id": "00000000-0000-0000-0000-000000000569", }, }, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000532", + "id": "00000000-0000-0000-0000-000000000553", }, "attachments": { - "id": "00000000-0000-0000-0000-000000000544", + "id": "00000000-0000-0000-0000-000000000565", }, "closeDate": { - "id": "00000000-0000-0000-0000-000000000533", + "id": "00000000-0000-0000-0000-000000000554", }, "company": { - "id": "00000000-0000-0000-0000-000000000535", + "id": "00000000-0000-0000-0000-000000000556", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000538", + "id": "00000000-0000-0000-0000-000000000559", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000539", + "id": "00000000-0000-0000-0000-000000000560", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000543", + "id": "00000000-0000-0000-0000-000000000564", }, "owner": { - "id": "00000000-0000-0000-0000-000000000537", + "id": "00000000-0000-0000-0000-000000000558", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000536", + "id": "00000000-0000-0000-0000-000000000557", }, "stage": { - "id": "00000000-0000-0000-0000-000000000534", + "id": "00000000-0000-0000-0000-000000000555", }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000542", + "id": "00000000-0000-0000-0000-000000000563", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000545", + "id": "00000000-0000-0000-0000-000000000566", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000540", + "id": "00000000-0000-0000-0000-000000000561", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000541", + "id": "00000000-0000-0000-0000-000000000562", }, }, "viewGroups": {}, @@ -1852,181 +1925,181 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "person": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000570", + "id": "00000000-0000-0000-0000-000000000591", }, "avatarFile": { - "id": "00000000-0000-0000-0000-000000000565", + "id": "00000000-0000-0000-0000-000000000586", }, "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000564", + "id": "00000000-0000-0000-0000-000000000585", }, "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000572", + "id": "00000000-0000-0000-0000-000000000593", }, "company": { - "id": "00000000-0000-0000-0000-000000000566", + "id": "00000000-0000-0000-0000-000000000587", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000552", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000555", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000554", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000560", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000551", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000562", - }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000561", - }, - "listMemberships": { - "id": "00000000-0000-0000-0000-000000000574", - }, - "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000571", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000559", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000569", - }, - "phones": { - "id": "00000000-0000-0000-0000-000000000563", - }, - "pointOfContactForOpportunities": { - "id": "00000000-0000-0000-0000-000000000567", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000557", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000558", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000568", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000573", }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000576", + }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000575", + }, + "emails": { + "id": "00000000-0000-0000-0000-000000000581", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000572", + }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000583", + }, + "linkedinLink": { + "id": "00000000-0000-0000-0000-000000000582", + }, + "listMemberships": { + "id": "00000000-0000-0000-0000-000000000595", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000592", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000580", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000590", + }, + "phones": { + "id": "00000000-0000-0000-0000-000000000584", + }, + "pointOfContactForOpportunities": { + "id": "00000000-0000-0000-0000-000000000588", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000578", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000579", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000589", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000594", + }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000553", + "id": "00000000-0000-0000-0000-000000000574", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000556", + "id": "00000000-0000-0000-0000-000000000577", }, }, - "id": "00000000-0000-0000-0000-000000000607", + "id": "00000000-0000-0000-0000-000000000628", "views": { "allPeople": { - "id": "00000000-0000-0000-0000-000000000583", + "id": "00000000-0000-0000-0000-000000000604", "viewFieldGroups": {}, "viewFields": { "company": { - "id": "00000000-0000-0000-0000-000000000578", + "id": "00000000-0000-0000-0000-000000000599", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000580", + "id": "00000000-0000-0000-0000-000000000601", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000577", + "id": "00000000-0000-0000-0000-000000000598", }, "emails": { - "id": "00000000-0000-0000-0000-000000000576", + "id": "00000000-0000-0000-0000-000000000597", }, "jobTitle": { - "id": "00000000-0000-0000-0000-000000000581", + "id": "00000000-0000-0000-0000-000000000602", }, "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000582", + "id": "00000000-0000-0000-0000-000000000603", }, "name": { - "id": "00000000-0000-0000-0000-000000000575", + "id": "00000000-0000-0000-0000-000000000596", }, "phones": { - "id": "00000000-0000-0000-0000-000000000579", + "id": "00000000-0000-0000-0000-000000000600", }, }, "viewGroups": {}, }, "personRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000606", + "id": "00000000-0000-0000-0000-000000000627", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000602", + "id": "00000000-0000-0000-0000-000000000623", }, "social": { - "id": "00000000-0000-0000-0000-000000000604", + "id": "00000000-0000-0000-0000-000000000625", }, "system": { - "id": "00000000-0000-0000-0000-000000000605", + "id": "00000000-0000-0000-0000-000000000626", }, "work": { - "id": "00000000-0000-0000-0000-000000000603", + "id": "00000000-0000-0000-0000-000000000624", }, }, "viewFields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000598", + "id": "00000000-0000-0000-0000-000000000619", }, "avatarFile": { - "id": "00000000-0000-0000-0000-000000000594", + "id": "00000000-0000-0000-0000-000000000615", }, "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000589", + "id": "00000000-0000-0000-0000-000000000610", }, "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000600", + "id": "00000000-0000-0000-0000-000000000621", }, "company": { - "id": "00000000-0000-0000-0000-000000000586", + "id": "00000000-0000-0000-0000-000000000607", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000590", + "id": "00000000-0000-0000-0000-000000000611", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000591", + "id": "00000000-0000-0000-0000-000000000612", }, "emails": { - "id": "00000000-0000-0000-0000-000000000584", + "id": "00000000-0000-0000-0000-000000000605", }, "jobTitle": { - "id": "00000000-0000-0000-0000-000000000587", + "id": "00000000-0000-0000-0000-000000000608", }, "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000588", + "id": "00000000-0000-0000-0000-000000000609", }, "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000599", + "id": "00000000-0000-0000-0000-000000000620", }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000597", + "id": "00000000-0000-0000-0000-000000000618", }, "phones": { - "id": "00000000-0000-0000-0000-000000000585", + "id": "00000000-0000-0000-0000-000000000606", }, "pointOfContactForOpportunities": { - "id": "00000000-0000-0000-0000-000000000595", + "id": "00000000-0000-0000-0000-000000000616", }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000596", + "id": "00000000-0000-0000-0000-000000000617", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000601", + "id": "00000000-0000-0000-0000-000000000622", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000592", + "id": "00000000-0000-0000-0000-000000000613", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000593", + "id": "00000000-0000-0000-0000-000000000614", }, }, "viewGroups": {}, @@ -2036,203 +2109,203 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "task": { "fields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000622", + "id": "00000000-0000-0000-0000-000000000643", }, "attachments": { - "id": "00000000-0000-0000-0000-000000000621", + "id": "00000000-0000-0000-0000-000000000642", }, "bodyV2": { - "id": "00000000-0000-0000-0000-000000000617", + "id": "00000000-0000-0000-0000-000000000638", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000609", + "id": "00000000-0000-0000-0000-000000000630", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000612", + "id": "00000000-0000-0000-0000-000000000633", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000611", + "id": "00000000-0000-0000-0000-000000000632", }, "dueAt": { - "id": "00000000-0000-0000-0000-000000000618", + "id": "00000000-0000-0000-0000-000000000639", }, "id": { - "id": "00000000-0000-0000-0000-000000000608", + "id": "00000000-0000-0000-0000-000000000629", }, "position": { - "id": "00000000-0000-0000-0000-000000000614", + "id": "00000000-0000-0000-0000-000000000635", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000615", + "id": "00000000-0000-0000-0000-000000000636", }, "status": { - "id": "00000000-0000-0000-0000-000000000619", + "id": "00000000-0000-0000-0000-000000000640", }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000620", + "id": "00000000-0000-0000-0000-000000000641", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000623", + "id": "00000000-0000-0000-0000-000000000644", }, "title": { - "id": "00000000-0000-0000-0000-000000000616", + "id": "00000000-0000-0000-0000-000000000637", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000610", + "id": "00000000-0000-0000-0000-000000000631", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000613", + "id": "00000000-0000-0000-0000-000000000634", }, }, - "id": "00000000-0000-0000-0000-000000000668", + "id": "00000000-0000-0000-0000-000000000689", "views": { "allTasks": { - "id": "00000000-0000-0000-0000-000000000632", - "viewFieldGroups": {}, - "viewFields": { - "assignee": { - "id": "00000000-0000-0000-0000-000000000629", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000630", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000631", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000627", - }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000628", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000625", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000626", - }, - "title": { - "id": "00000000-0000-0000-0000-000000000624", - }, - }, - "viewGroups": {}, - }, - "assignedToMe": { - "id": "00000000-0000-0000-0000-000000000644", - "viewFieldGroups": {}, - "viewFields": { - "assignee": { - "id": "00000000-0000-0000-0000-000000000637", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000638", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000639", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000635", - }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000636", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000634", - }, - "title": { - "id": "00000000-0000-0000-0000-000000000633", - }, - }, - "viewGroups": { - "done": { - "id": "00000000-0000-0000-0000-000000000642", - }, - "empty": { - "id": "00000000-0000-0000-0000-000000000643", - }, - "inProgress": { - "id": "00000000-0000-0000-0000-000000000641", - }, - "todo": { - "id": "00000000-0000-0000-0000-000000000640", - }, - }, - }, - "byStatus": { "id": "00000000-0000-0000-0000-000000000653", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000648", + "id": "00000000-0000-0000-0000-000000000650", + }, + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000651", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000649", + "id": "00000000-0000-0000-0000-000000000652", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000648", }, "dueAt": { - "id": "00000000-0000-0000-0000-000000000647", + "id": "00000000-0000-0000-0000-000000000649", }, "status": { "id": "00000000-0000-0000-0000-000000000646", }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000647", + }, "title": { "id": "00000000-0000-0000-0000-000000000645", }, }, + "viewGroups": {}, + }, + "assignedToMe": { + "id": "00000000-0000-0000-0000-000000000665", + "viewFieldGroups": {}, + "viewFields": { + "assignee": { + "id": "00000000-0000-0000-0000-000000000658", + }, + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000659", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000660", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000656", + }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000657", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000655", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000654", + }, + }, "viewGroups": { "done": { - "id": "00000000-0000-0000-0000-000000000652", + "id": "00000000-0000-0000-0000-000000000663", + }, + "empty": { + "id": "00000000-0000-0000-0000-000000000664", }, "inProgress": { - "id": "00000000-0000-0000-0000-000000000651", + "id": "00000000-0000-0000-0000-000000000662", }, "todo": { - "id": "00000000-0000-0000-0000-000000000650", + "id": "00000000-0000-0000-0000-000000000661", + }, + }, + }, + "byStatus": { + "id": "00000000-0000-0000-0000-000000000674", + "viewFieldGroups": {}, + "viewFields": { + "assignee": { + "id": "00000000-0000-0000-0000-000000000669", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000670", + }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000668", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000667", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000666", + }, + }, + "viewGroups": { + "done": { + "id": "00000000-0000-0000-0000-000000000673", + }, + "inProgress": { + "id": "00000000-0000-0000-0000-000000000672", + }, + "todo": { + "id": "00000000-0000-0000-0000-000000000671", }, }, }, "taskRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000667", + "id": "00000000-0000-0000-0000-000000000688", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000665", + "id": "00000000-0000-0000-0000-000000000686", }, "system": { - "id": "00000000-0000-0000-0000-000000000666", + "id": "00000000-0000-0000-0000-000000000687", }, }, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000656", + "id": "00000000-0000-0000-0000-000000000677", }, "attachments": { - "id": "00000000-0000-0000-0000-000000000663", + "id": "00000000-0000-0000-0000-000000000684", }, "bodyV2": { - "id": "00000000-0000-0000-0000-000000000660", + "id": "00000000-0000-0000-0000-000000000681", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000657", + "id": "00000000-0000-0000-0000-000000000678", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000658", + "id": "00000000-0000-0000-0000-000000000679", }, "dueAt": { - "id": "00000000-0000-0000-0000-000000000654", + "id": "00000000-0000-0000-0000-000000000675", }, "status": { - "id": "00000000-0000-0000-0000-000000000655", + "id": "00000000-0000-0000-0000-000000000676", }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000659", + "id": "00000000-0000-0000-0000-000000000680", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000664", + "id": "00000000-0000-0000-0000-000000000685", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000661", + "id": "00000000-0000-0000-0000-000000000682", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000662", + "id": "00000000-0000-0000-0000-000000000683", }, }, "viewGroups": {}, @@ -2242,62 +2315,62 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "taskTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000670", + "id": "00000000-0000-0000-0000-000000000691", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000673", + "id": "00000000-0000-0000-0000-000000000694", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000672", + "id": "00000000-0000-0000-0000-000000000693", }, "id": { - "id": "00000000-0000-0000-0000-000000000669", + "id": "00000000-0000-0000-0000-000000000690", }, "position": { - "id": "00000000-0000-0000-0000-000000000675", + "id": "00000000-0000-0000-0000-000000000696", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000676", + "id": "00000000-0000-0000-0000-000000000697", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000679", + "id": "00000000-0000-0000-0000-000000000700", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000680", + "id": "00000000-0000-0000-0000-000000000701", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000678", + "id": "00000000-0000-0000-0000-000000000699", }, "task": { - "id": "00000000-0000-0000-0000-000000000677", + "id": "00000000-0000-0000-0000-000000000698", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000671", + "id": "00000000-0000-0000-0000-000000000692", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000674", + "id": "00000000-0000-0000-0000-000000000695", }, }, - "id": "00000000-0000-0000-0000-000000000687", + "id": "00000000-0000-0000-0000-000000000708", "views": { "allTaskTargets": { - "id": "00000000-0000-0000-0000-000000000686", + "id": "00000000-0000-0000-0000-000000000707", "viewFieldGroups": {}, "viewFields": { "id": { - "id": "00000000-0000-0000-0000-000000000681", + "id": "00000000-0000-0000-0000-000000000702", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000684", + "id": "00000000-0000-0000-0000-000000000705", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000685", + "id": "00000000-0000-0000-0000-000000000706", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000683", + "id": "00000000-0000-0000-0000-000000000704", }, "task": { - "id": "00000000-0000-0000-0000-000000000682", + "id": "00000000-0000-0000-0000-000000000703", }, }, "viewGroups": {}, @@ -2307,131 +2380,131 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "timelineActivity": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000689", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000692", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000691", - }, - "happensAt": { - "id": "00000000-0000-0000-0000-000000000697", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000688", - }, - "linkedObjectMetadataId": { - "id": "00000000-0000-0000-0000-000000000713", - }, - "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000711", - }, - "linkedRecordId": { - "id": "00000000-0000-0000-0000-000000000712", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000696", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000694", - }, - "properties": { - "id": "00000000-0000-0000-0000-000000000698", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000695", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000701", - }, - "targetDashboard": { - "id": "00000000-0000-0000-0000-000000000708", - }, - "targetMessageCampaign": { "id": "00000000-0000-0000-0000-000000000710", }, - "targetMessageList": { + "createdBy": { + "id": "00000000-0000-0000-0000-000000000713", + }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000712", + }, + "happensAt": { + "id": "00000000-0000-0000-0000-000000000718", + }, + "id": { "id": "00000000-0000-0000-0000-000000000709", }, + "linkedObjectMetadataId": { + "id": "00000000-0000-0000-0000-000000000734", + }, + "linkedRecordCachedName": { + "id": "00000000-0000-0000-0000-000000000732", + }, + "linkedRecordId": { + "id": "00000000-0000-0000-0000-000000000733", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000717", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000715", + }, + "properties": { + "id": "00000000-0000-0000-0000-000000000719", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000716", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000722", + }, + "targetDashboard": { + "id": "00000000-0000-0000-0000-000000000729", + }, + "targetMessageCampaign": { + "id": "00000000-0000-0000-0000-000000000731", + }, + "targetMessageList": { + "id": "00000000-0000-0000-0000-000000000730", + }, "targetNote": { - "id": "00000000-0000-0000-0000-000000000704", + "id": "00000000-0000-0000-0000-000000000725", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000702", + "id": "00000000-0000-0000-0000-000000000723", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000700", + "id": "00000000-0000-0000-0000-000000000721", }, "targetTask": { - "id": "00000000-0000-0000-0000-000000000703", + "id": "00000000-0000-0000-0000-000000000724", }, "targetWorkflow": { - "id": "00000000-0000-0000-0000-000000000705", + "id": "00000000-0000-0000-0000-000000000726", }, "targetWorkflowRun": { - "id": "00000000-0000-0000-0000-000000000707", + "id": "00000000-0000-0000-0000-000000000728", }, "targetWorkflowVersion": { - "id": "00000000-0000-0000-0000-000000000706", + "id": "00000000-0000-0000-0000-000000000727", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000690", + "id": "00000000-0000-0000-0000-000000000711", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000693", + "id": "00000000-0000-0000-0000-000000000714", }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000699", + "id": "00000000-0000-0000-0000-000000000720", }, }, - "id": "00000000-0000-0000-0000-000000000729", + "id": "00000000-0000-0000-0000-000000000750", "views": { "allTimelineActivities": { - "id": "00000000-0000-0000-0000-000000000728", + "id": "00000000-0000-0000-0000-000000000749", "viewFieldGroups": {}, "viewFields": { "happensAt": { - "id": "00000000-0000-0000-0000-000000000715", + "id": "00000000-0000-0000-0000-000000000736", }, "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000718", + "id": "00000000-0000-0000-0000-000000000739", }, "name": { - "id": "00000000-0000-0000-0000-000000000714", + "id": "00000000-0000-0000-0000-000000000735", }, "properties": { - "id": "00000000-0000-0000-0000-000000000716", + "id": "00000000-0000-0000-0000-000000000737", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000720", + "id": "00000000-0000-0000-0000-000000000741", }, "targetDashboard": { - "id": "00000000-0000-0000-0000-000000000727", + "id": "00000000-0000-0000-0000-000000000748", }, "targetNote": { - "id": "00000000-0000-0000-0000-000000000723", + "id": "00000000-0000-0000-0000-000000000744", }, "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000721", + "id": "00000000-0000-0000-0000-000000000742", }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000719", + "id": "00000000-0000-0000-0000-000000000740", }, "targetTask": { - "id": "00000000-0000-0000-0000-000000000722", + "id": "00000000-0000-0000-0000-000000000743", }, "targetWorkflow": { - "id": "00000000-0000-0000-0000-000000000724", + "id": "00000000-0000-0000-0000-000000000745", }, "targetWorkflowRun": { - "id": "00000000-0000-0000-0000-000000000726", + "id": "00000000-0000-0000-0000-000000000747", }, "targetWorkflowVersion": { - "id": "00000000-0000-0000-0000-000000000725", + "id": "00000000-0000-0000-0000-000000000746", }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000717", + "id": "00000000-0000-0000-0000-000000000738", }, }, "viewGroups": {}, @@ -2441,77 +2514,77 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflow": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000745", + "id": "00000000-0000-0000-0000-000000000766", }, "automatedTriggers": { - "id": "00000000-0000-0000-0000-000000000743", + "id": "00000000-0000-0000-0000-000000000764", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000731", + "id": "00000000-0000-0000-0000-000000000752", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000734", + "id": "00000000-0000-0000-0000-000000000755", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000733", + "id": "00000000-0000-0000-0000-000000000754", }, "id": { - "id": "00000000-0000-0000-0000-000000000730", + "id": "00000000-0000-0000-0000-000000000751", }, "lastPublishedVersionId": { - "id": "00000000-0000-0000-0000-000000000739", + "id": "00000000-0000-0000-0000-000000000760", }, "name": { - "id": "00000000-0000-0000-0000-000000000738", + "id": "00000000-0000-0000-0000-000000000759", }, "position": { - "id": "00000000-0000-0000-0000-000000000736", + "id": "00000000-0000-0000-0000-000000000757", }, "runs": { - "id": "00000000-0000-0000-0000-000000000742", + "id": "00000000-0000-0000-0000-000000000763", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000737", + "id": "00000000-0000-0000-0000-000000000758", }, "statuses": { - "id": "00000000-0000-0000-0000-000000000740", + "id": "00000000-0000-0000-0000-000000000761", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000744", + "id": "00000000-0000-0000-0000-000000000765", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000732", + "id": "00000000-0000-0000-0000-000000000753", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000735", + "id": "00000000-0000-0000-0000-000000000756", }, "versions": { - "id": "00000000-0000-0000-0000-000000000741", + "id": "00000000-0000-0000-0000-000000000762", }, }, - "id": "00000000-0000-0000-0000-000000000753", + "id": "00000000-0000-0000-0000-000000000774", "views": { "allWorkflows": { - "id": "00000000-0000-0000-0000-000000000752", + "id": "00000000-0000-0000-0000-000000000773", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000749", + "id": "00000000-0000-0000-0000-000000000770", }, "name": { - "id": "00000000-0000-0000-0000-000000000746", + "id": "00000000-0000-0000-0000-000000000767", }, "runs": { - "id": "00000000-0000-0000-0000-000000000751", + "id": "00000000-0000-0000-0000-000000000772", }, "statuses": { - "id": "00000000-0000-0000-0000-000000000747", + "id": "00000000-0000-0000-0000-000000000768", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000748", + "id": "00000000-0000-0000-0000-000000000769", }, "versions": { - "id": "00000000-0000-0000-0000-000000000750", + "id": "00000000-0000-0000-0000-000000000771", }, }, "viewGroups": {}, @@ -2521,79 +2594,79 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowAutomatedTrigger": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000755", + "id": "00000000-0000-0000-0000-000000000776", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000758", + "id": "00000000-0000-0000-0000-000000000779", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000757", + "id": "00000000-0000-0000-0000-000000000778", }, "id": { - "id": "00000000-0000-0000-0000-000000000754", + "id": "00000000-0000-0000-0000-000000000775", }, "position": { - "id": "00000000-0000-0000-0000-000000000760", + "id": "00000000-0000-0000-0000-000000000781", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000761", + "id": "00000000-0000-0000-0000-000000000782", }, "settings": { - "id": "00000000-0000-0000-0000-000000000763", + "id": "00000000-0000-0000-0000-000000000784", }, "type": { - "id": "00000000-0000-0000-0000-000000000762", + "id": "00000000-0000-0000-0000-000000000783", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000756", + "id": "00000000-0000-0000-0000-000000000777", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000759", + "id": "00000000-0000-0000-0000-000000000780", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000764", + "id": "00000000-0000-0000-0000-000000000785", }, }, - "id": "00000000-0000-0000-0000-000000000776", + "id": "00000000-0000-0000-0000-000000000797", "views": { "allWorkflowAutomatedTriggers": { - "id": "00000000-0000-0000-0000-000000000768", + "id": "00000000-0000-0000-0000-000000000789", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000767", + "id": "00000000-0000-0000-0000-000000000788", }, "type": { - "id": "00000000-0000-0000-0000-000000000765", + "id": "00000000-0000-0000-0000-000000000786", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000766", + "id": "00000000-0000-0000-0000-000000000787", }, }, "viewGroups": {}, }, "workflowAutomatedTriggerRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000775", + "id": "00000000-0000-0000-0000-000000000796", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000773", + "id": "00000000-0000-0000-0000-000000000794", }, "system": { - "id": "00000000-0000-0000-0000-000000000774", + "id": "00000000-0000-0000-0000-000000000795", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000771", + "id": "00000000-0000-0000-0000-000000000792", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000772", + "id": "00000000-0000-0000-0000-000000000793", }, "type": { - "id": "00000000-0000-0000-0000-000000000769", + "id": "00000000-0000-0000-0000-000000000790", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000770", + "id": "00000000-0000-0000-0000-000000000791", }, }, "viewGroups": {}, @@ -2603,133 +2676,133 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowRun": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000778", + "id": "00000000-0000-0000-0000-000000000799", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000781", + "id": "00000000-0000-0000-0000-000000000802", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000780", + "id": "00000000-0000-0000-0000-000000000801", }, "endedAt": { - "id": "00000000-0000-0000-0000-000000000790", + "id": "00000000-0000-0000-0000-000000000811", }, "enqueuedAt": { - "id": "00000000-0000-0000-0000-000000000788", + "id": "00000000-0000-0000-0000-000000000809", }, "id": { - "id": "00000000-0000-0000-0000-000000000777", + "id": "00000000-0000-0000-0000-000000000798", }, "name": { - "id": "00000000-0000-0000-0000-000000000785", + "id": "00000000-0000-0000-0000-000000000806", }, "position": { - "id": "00000000-0000-0000-0000-000000000783", + "id": "00000000-0000-0000-0000-000000000804", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000784", + "id": "00000000-0000-0000-0000-000000000805", }, "startedAt": { - "id": "00000000-0000-0000-0000-000000000789", + "id": "00000000-0000-0000-0000-000000000810", }, "state": { - "id": "00000000-0000-0000-0000-000000000792", + "id": "00000000-0000-0000-0000-000000000813", }, "status": { - "id": "00000000-0000-0000-0000-000000000791", + "id": "00000000-0000-0000-0000-000000000812", }, "stepLogs": { - "id": "00000000-0000-0000-0000-000000000793", + "id": "00000000-0000-0000-0000-000000000814", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000794", + "id": "00000000-0000-0000-0000-000000000815", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000779", + "id": "00000000-0000-0000-0000-000000000800", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000782", + "id": "00000000-0000-0000-0000-000000000803", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000787", + "id": "00000000-0000-0000-0000-000000000808", }, "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000786", + "id": "00000000-0000-0000-0000-000000000807", }, }, - "id": "00000000-0000-0000-0000-000000000817", + "id": "00000000-0000-0000-0000-000000000838", "views": { "allWorkflowRuns": { - "id": "00000000-0000-0000-0000-000000000801", + "id": "00000000-0000-0000-0000-000000000822", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000799", + "id": "00000000-0000-0000-0000-000000000820", }, "name": { - "id": "00000000-0000-0000-0000-000000000795", + "id": "00000000-0000-0000-0000-000000000816", }, "startedAt": { - "id": "00000000-0000-0000-0000-000000000798", + "id": "00000000-0000-0000-0000-000000000819", }, "status": { - "id": "00000000-0000-0000-0000-000000000797", + "id": "00000000-0000-0000-0000-000000000818", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000796", + "id": "00000000-0000-0000-0000-000000000817", }, "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000800", + "id": "00000000-0000-0000-0000-000000000821", }, }, "viewGroups": {}, }, "workflowRunRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000816", + "id": "00000000-0000-0000-0000-000000000837", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000814", + "id": "00000000-0000-0000-0000-000000000835", }, "system": { - "id": "00000000-0000-0000-0000-000000000815", + "id": "00000000-0000-0000-0000-000000000836", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000807", + "id": "00000000-0000-0000-0000-000000000828", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000808", + "id": "00000000-0000-0000-0000-000000000829", }, "endedAt": { - "id": "00000000-0000-0000-0000-000000000806", + "id": "00000000-0000-0000-0000-000000000827", }, "enqueuedAt": { - "id": "00000000-0000-0000-0000-000000000809", + "id": "00000000-0000-0000-0000-000000000830", }, "startedAt": { - "id": "00000000-0000-0000-0000-000000000805", + "id": "00000000-0000-0000-0000-000000000826", }, "state": { - "id": "00000000-0000-0000-0000-000000000810", + "id": "00000000-0000-0000-0000-000000000831", }, "status": { - "id": "00000000-0000-0000-0000-000000000802", + "id": "00000000-0000-0000-0000-000000000823", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000813", + "id": "00000000-0000-0000-0000-000000000834", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000811", + "id": "00000000-0000-0000-0000-000000000832", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000812", + "id": "00000000-0000-0000-0000-000000000833", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000803", + "id": "00000000-0000-0000-0000-000000000824", }, "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000804", + "id": "00000000-0000-0000-0000-000000000825", }, }, "viewGroups": {}, @@ -2739,115 +2812,115 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowVersion": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000819", + "id": "00000000-0000-0000-0000-000000000840", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000822", + "id": "00000000-0000-0000-0000-000000000843", }, "deletedAt": { - "id": "00000000-0000-0000-0000-000000000821", + "id": "00000000-0000-0000-0000-000000000842", }, "id": { - "id": "00000000-0000-0000-0000-000000000818", + "id": "00000000-0000-0000-0000-000000000839", }, "name": { - "id": "00000000-0000-0000-0000-000000000826", + "id": "00000000-0000-0000-0000-000000000847", }, "position": { - "id": "00000000-0000-0000-0000-000000000824", + "id": "00000000-0000-0000-0000-000000000845", }, "runs": { - "id": "00000000-0000-0000-0000-000000000830", + "id": "00000000-0000-0000-0000-000000000851", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000825", + "id": "00000000-0000-0000-0000-000000000846", }, "status": { - "id": "00000000-0000-0000-0000-000000000829", + "id": "00000000-0000-0000-0000-000000000850", }, "steps": { - "id": "00000000-0000-0000-0000-000000000831", + "id": "00000000-0000-0000-0000-000000000852", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000832", + "id": "00000000-0000-0000-0000-000000000853", }, "trigger": { - "id": "00000000-0000-0000-0000-000000000828", + "id": "00000000-0000-0000-0000-000000000849", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000820", + "id": "00000000-0000-0000-0000-000000000841", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000823", + "id": "00000000-0000-0000-0000-000000000844", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000827", + "id": "00000000-0000-0000-0000-000000000848", }, }, - "id": "00000000-0000-0000-0000-000000000852", + "id": "00000000-0000-0000-0000-000000000873", "views": { "allWorkflowVersions": { - "id": "00000000-0000-0000-0000-000000000838", + "id": "00000000-0000-0000-0000-000000000859", "viewFieldGroups": {}, "viewFields": { "name": { - "id": "00000000-0000-0000-0000-000000000833", + "id": "00000000-0000-0000-0000-000000000854", }, "runs": { - "id": "00000000-0000-0000-0000-000000000837", + "id": "00000000-0000-0000-0000-000000000858", }, "status": { - "id": "00000000-0000-0000-0000-000000000835", + "id": "00000000-0000-0000-0000-000000000856", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000836", + "id": "00000000-0000-0000-0000-000000000857", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000834", + "id": "00000000-0000-0000-0000-000000000855", }, }, "viewGroups": {}, }, "workflowVersionRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000851", + "id": "00000000-0000-0000-0000-000000000872", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000849", + "id": "00000000-0000-0000-0000-000000000870", }, "system": { - "id": "00000000-0000-0000-0000-000000000850", + "id": "00000000-0000-0000-0000-000000000871", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000842", + "id": "00000000-0000-0000-0000-000000000863", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000844", + "id": "00000000-0000-0000-0000-000000000865", }, "runs": { - "id": "00000000-0000-0000-0000-000000000847", + "id": "00000000-0000-0000-0000-000000000868", }, "status": { - "id": "00000000-0000-0000-0000-000000000839", + "id": "00000000-0000-0000-0000-000000000860", }, "steps": { - "id": "00000000-0000-0000-0000-000000000843", + "id": "00000000-0000-0000-0000-000000000864", }, "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000848", + "id": "00000000-0000-0000-0000-000000000869", }, "trigger": { - "id": "00000000-0000-0000-0000-000000000841", + "id": "00000000-0000-0000-0000-000000000862", }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000845", + "id": "00000000-0000-0000-0000-000000000866", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000846", + "id": "00000000-0000-0000-0000-000000000867", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000840", + "id": "00000000-0000-0000-0000-000000000861", }, }, "viewGroups": {}, @@ -2857,125 +2930,125 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workspaceMember": { "fields": { "accountOwnerForCompanies": { - "id": "00000000-0000-0000-0000-000000000870", + "id": "00000000-0000-0000-0000-000000000891", }, "assignedTasks": { - "id": "00000000-0000-0000-0000-000000000868", + "id": "00000000-0000-0000-0000-000000000889", }, "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000864", + "id": "00000000-0000-0000-0000-000000000885", }, "blocklist": { - "id": "00000000-0000-0000-0000-000000000872", + "id": "00000000-0000-0000-0000-000000000893", }, "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000873", + "id": "00000000-0000-0000-0000-000000000894", }, "calendarStartDay": { - "id": "00000000-0000-0000-0000-000000000878", + "id": "00000000-0000-0000-0000-000000000899", }, "colorScheme": { - "id": "00000000-0000-0000-0000-000000000862", + "id": "00000000-0000-0000-0000-000000000883", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000854", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000857", - }, - "dateFormat": { - "id": "00000000-0000-0000-0000-000000000876", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000856", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000853", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000866", - }, - "locale": { - "id": "00000000-0000-0000-0000-000000000863", - }, - "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000871", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000861", - }, - "numberFormat": { - "id": "00000000-0000-0000-0000-000000000879", - }, - "ownedOpportunities": { - "id": "00000000-0000-0000-0000-000000000869", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000859", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000860", - }, - "timeFormat": { - "id": "00000000-0000-0000-0000-000000000877", - }, - "timeZone": { "id": "00000000-0000-0000-0000-000000000875", }, - "timelineActivities": { + "createdBy": { + "id": "00000000-0000-0000-0000-000000000878", + }, + "dateFormat": { + "id": "00000000-0000-0000-0000-000000000897", + }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000877", + }, + "id": { "id": "00000000-0000-0000-0000-000000000874", }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000887", + }, + "locale": { + "id": "00000000-0000-0000-0000-000000000884", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000892", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000882", + }, + "numberFormat": { + "id": "00000000-0000-0000-0000-000000000900", + }, + "ownedOpportunities": { + "id": "00000000-0000-0000-0000-000000000890", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000880", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000881", + }, + "timeFormat": { + "id": "00000000-0000-0000-0000-000000000898", + }, + "timeZone": { + "id": "00000000-0000-0000-0000-000000000896", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000895", + }, "updatedAt": { - "id": "00000000-0000-0000-0000-000000000855", + "id": "00000000-0000-0000-0000-000000000876", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000858", + "id": "00000000-0000-0000-0000-000000000879", }, "userEmail": { - "id": "00000000-0000-0000-0000-000000000865", + "id": "00000000-0000-0000-0000-000000000886", }, "userId": { - "id": "00000000-0000-0000-0000-000000000867", + "id": "00000000-0000-0000-0000-000000000888", }, }, - "id": "00000000-0000-0000-0000-000000000892", + "id": "00000000-0000-0000-0000-000000000913", "views": { "allWorkspaceMembers": { - "id": "00000000-0000-0000-0000-000000000891", + "id": "00000000-0000-0000-0000-000000000912", "viewFieldGroups": {}, "viewFields": { "assignedTasks": { - "id": "00000000-0000-0000-0000-000000000890", + "id": "00000000-0000-0000-0000-000000000911", }, "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000882", + "id": "00000000-0000-0000-0000-000000000903", }, "colorScheme": { - "id": "00000000-0000-0000-0000-000000000883", + "id": "00000000-0000-0000-0000-000000000904", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000888", + "id": "00000000-0000-0000-0000-000000000909", }, "dateFormat": { - "id": "00000000-0000-0000-0000-000000000886", + "id": "00000000-0000-0000-0000-000000000907", }, "locale": { - "id": "00000000-0000-0000-0000-000000000884", + "id": "00000000-0000-0000-0000-000000000905", }, "name": { - "id": "00000000-0000-0000-0000-000000000880", + "id": "00000000-0000-0000-0000-000000000901", }, "ownedOpportunities": { - "id": "00000000-0000-0000-0000-000000000889", + "id": "00000000-0000-0000-0000-000000000910", }, "timeFormat": { - "id": "00000000-0000-0000-0000-000000000887", + "id": "00000000-0000-0000-0000-000000000908", }, "timeZone": { - "id": "00000000-0000-0000-0000-000000000885", + "id": "00000000-0000-0000-0000-000000000906", }, "userEmail": { - "id": "00000000-0000-0000-0000-000000000881", + "id": "00000000-0000-0000-0000-000000000902", }, }, "viewGroups": {}, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts index 19120aaace..075bb089d3 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts @@ -261,6 +261,58 @@ export const buildMessageCampaignStandardFlatFieldMetadatas = ({ settings: { displayFormat: DateDisplayFormat.RELATIVE }, }, }), + sentCount: createStandardFieldFlatMetadata({ + ...base, + context: { + fieldName: 'sentCount', + type: FieldMetadataType.NUMBER, + label: i18nLabel(msg`Sent count`), + description: i18nLabel(msg`Number of emails sent`), + icon: 'IconMailFast', + isNullable: false, + isUIEditable: false, + defaultValue: 0, + }, + }), + failedCount: createStandardFieldFlatMetadata({ + ...base, + context: { + fieldName: 'failedCount', + type: FieldMetadataType.NUMBER, + label: i18nLabel(msg`Failed count`), + description: i18nLabel(msg`Number of emails that failed to send`), + icon: 'IconMailX', + isNullable: false, + isUIEditable: false, + defaultValue: 0, + }, + }), + bouncedCount: createStandardFieldFlatMetadata({ + ...base, + context: { + fieldName: 'bouncedCount', + type: FieldMetadataType.NUMBER, + label: i18nLabel(msg`Bounced count`), + description: i18nLabel(msg`Number of emails that bounced`), + icon: 'IconMailOff', + isNullable: false, + isUIEditable: false, + defaultValue: 0, + }, + }), + complainedCount: createStandardFieldFlatMetadata({ + ...base, + context: { + fieldName: 'complainedCount', + type: FieldMetadataType.NUMBER, + label: i18nLabel(msg`Complained count`), + description: i18nLabel(msg`Number of spam complaints received`), + icon: 'IconMoodSad', + isNullable: false, + isUIEditable: false, + defaultValue: 0, + }, + }), unsubscribeTopicId: createStandardFieldFlatMetadata({ ...base, context: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/build-standard-flat-view-field-metadata-maps.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/build-standard-flat-view-field-metadata-maps.util.ts index 9c7ad457a0..f8a6caf48c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/build-standard-flat-view-field-metadata-maps.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/build-standard-flat-view-field-metadata-maps.util.ts @@ -11,8 +11,10 @@ import { computeStandardCalendarEventViewFields } from 'src/engine/workspace-man import { computeStandardCallRecordingViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-call-recording-view-fields.util'; import { computeStandardCompanyViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-company-view-fields.util'; import { computeStandardDashboardViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-dashboard-view-fields.util'; +import { computeStandardMessageCampaignViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-campaign-view-fields.util'; import { computeStandardMessageChannelMessageAssociationMessageFolderViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-channel-message-association-message-folder-view-fields.util'; import { computeStandardMessageChannelMessageAssociationViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-channel-message-association-view-fields.util'; +import { computeStandardMessageListViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-list-view-fields.util'; import { computeStandardMessageParticipantViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-participant-view-fields.util'; import { computeStandardMessageThreadViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-thread-view-fields.util'; import { computeStandardMessageViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-view-fields.util'; @@ -45,10 +47,12 @@ const STANDARD_FLAT_VIEW_FIELD_METADATA_BUILDERS_BY_OBJECT_NAME = { company: computeStandardCompanyViewFields, dashboard: computeStandardDashboardViewFields, message: computeStandardMessageViewFields, + messageCampaign: computeStandardMessageCampaignViewFields, messageChannelMessageAssociation: computeStandardMessageChannelMessageAssociationViewFields, messageChannelMessageAssociationMessageFolder: computeStandardMessageChannelMessageAssociationMessageFolderViewFields, + messageList: computeStandardMessageListViewFields, messageParticipant: computeStandardMessageParticipantViewFields, messageThread: computeStandardMessageThreadViewFields, note: computeStandardNoteViewFields, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-campaign-view-fields.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-campaign-view-fields.util.ts new file mode 100644 index 0000000000..0985d86c4d --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-campaign-view-fields.util.ts @@ -0,0 +1,144 @@ +import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { + createStandardViewFieldFlatMetadata, + type CreateStandardViewFieldArgs, +} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util'; + +export const computeStandardMessageCampaignViewFields = ( + args: Omit, 'context'>, +): Record => { + return { + allMessageCampaignsSubject: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'subject', + fieldName: 'subject', + position: 0, + isVisible: true, + size: 300, + }, + }), + allMessageCampaignsStatus: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'status', + fieldName: 'status', + position: 1, + isVisible: true, + size: 120, + }, + }), + allMessageCampaignsList: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'list', + fieldName: 'list', + position: 2, + isVisible: true, + size: 150, + }, + }), + allMessageCampaignsFromAddress: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'fromAddress', + fieldName: 'fromAddress', + position: 3, + isVisible: true, + size: 200, + }, + }), + allMessageCampaignsSentAt: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'sentAt', + fieldName: 'sentAt', + position: 4, + isVisible: true, + size: 150, + }, + }), + allMessageCampaignsSentCount: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'sentCount', + fieldName: 'sentCount', + position: 5, + isVisible: true, + size: 100, + }, + }), + allMessageCampaignsFailedCount: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'failedCount', + fieldName: 'failedCount', + position: 6, + isVisible: true, + size: 100, + }, + }), + allMessageCampaignsBouncedCount: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'bouncedCount', + fieldName: 'bouncedCount', + position: 7, + isVisible: true, + size: 100, + }, + }), + allMessageCampaignsComplainedCount: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'complainedCount', + fieldName: 'complainedCount', + position: 8, + isVisible: true, + size: 120, + }, + }), + allMessageCampaignsRecipients: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'recipients', + fieldName: 'recipients', + position: 9, + isVisible: true, + size: 150, + }, + }), + allMessageCampaignsCreatedAt: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + viewFieldName: 'createdAt', + fieldName: 'createdAt', + position: 10, + isVisible: true, + size: 150, + }, + }), + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-list-view-fields.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-list-view-fields.util.ts new file mode 100644 index 0000000000..d3bd3dcbac --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-list-view-fields.util.ts @@ -0,0 +1,60 @@ +import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { + createStandardViewFieldFlatMetadata, + type CreateStandardViewFieldArgs, +} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util'; + +export const computeStandardMessageListViewFields = ( + args: Omit, 'context'>, +): Record => { + return { + allMessageListsName: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageList', + context: { + viewName: 'allMessageLists', + viewFieldName: 'name', + fieldName: 'name', + position: 0, + isVisible: true, + size: 300, + }, + }), + allMessageListsMembers: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageList', + context: { + viewName: 'allMessageLists', + viewFieldName: 'members', + fieldName: 'members', + position: 1, + isVisible: true, + size: 150, + }, + }), + allMessageListsCampaigns: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageList', + context: { + viewName: 'allMessageLists', + viewFieldName: 'campaigns', + fieldName: 'campaigns', + position: 2, + isVisible: true, + size: 150, + }, + }), + allMessageListsCreatedAt: createStandardViewFieldFlatMetadata({ + ...args, + objectName: 'messageList', + context: { + viewName: 'allMessageLists', + viewFieldName: 'createdAt', + fieldName: 'createdAt', + position: 3, + isVisible: true, + size: 150, + }, + }), + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/build-standard-flat-view-metadata-maps.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/build-standard-flat-view-metadata-maps.util.ts index 9f519ae1b6..fde7a7fe42 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/build-standard-flat-view-metadata-maps.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/build-standard-flat-view-metadata-maps.util.ts @@ -11,8 +11,10 @@ import { computeStandardCalendarEventViews } from 'src/engine/workspace-manager/ import { computeStandardCallRecordingViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-call-recording-views.util'; import { computeStandardCompanyViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-company-views.util'; import { computeStandardDashboardViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-dashboard-views.util'; +import { computeStandardMessageCampaignViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-campaign-views.util'; import { computeStandardMessageChannelMessageAssociationMessageFolderViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-channel-message-association-message-folder-views.util'; import { computeStandardMessageChannelMessageAssociationViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-channel-message-association-views.util'; +import { computeStandardMessageListViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-list-views.util'; import { computeStandardMessageParticipantViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-participant-views.util'; import { computeStandardMessageThreadViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-thread-views.util'; import { computeStandardMessageViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-views.util'; @@ -45,10 +47,12 @@ const STANDARD_FLAT_VIEW_METADATA_BUILDERS_BY_OBJECT_NAME = { company: computeStandardCompanyViews, dashboard: computeStandardDashboardViews, message: computeStandardMessageViews, + messageCampaign: computeStandardMessageCampaignViews, messageChannelMessageAssociation: computeStandardMessageChannelMessageAssociationViews, messageChannelMessageAssociationMessageFolder: computeStandardMessageChannelMessageAssociationMessageFolderViews, + messageList: computeStandardMessageListViews, messageParticipant: computeStandardMessageParticipantViews, messageThread: computeStandardMessageThreadViews, note: computeStandardNoteViews, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-campaign-views.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-campaign-views.util.ts new file mode 100644 index 0000000000..a0fbfea52f --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-campaign-views.util.ts @@ -0,0 +1,26 @@ +import { ViewType, ViewKey } from 'twenty-shared/types'; + +import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; +import { + createStandardViewFlatMetadata, + type CreateStandardViewArgs, +} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util'; + +export const computeStandardMessageCampaignViews = ( + args: Omit, 'context'>, +): Record => { + return { + allMessageCampaigns: createStandardViewFlatMetadata({ + ...args, + objectName: 'messageCampaign', + context: { + viewName: 'allMessageCampaigns', + name: 'All {objectLabelPlural}', + type: ViewType.TABLE, + key: ViewKey.INDEX, + position: 0, + icon: 'IconSend', + }, + }), + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-list-views.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-list-views.util.ts new file mode 100644 index 0000000000..97c7420d9f --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-list-views.util.ts @@ -0,0 +1,26 @@ +import { ViewType, ViewKey } from 'twenty-shared/types'; + +import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; +import { + createStandardViewFlatMetadata, + type CreateStandardViewArgs, +} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util'; + +export const computeStandardMessageListViews = ( + args: Omit, 'context'>, +): Record => { + return { + allMessageLists: createStandardViewFlatMetadata({ + ...args, + objectName: 'messageList', + context: { + viewName: 'allMessageLists', + name: 'All {objectLabelPlural}', + type: ViewType.TABLE, + key: ViewKey.INDEX, + position: 0, + icon: 'IconList', + }, + }), + }; +}; diff --git a/packages/twenty-server/src/modules/emailing/emailing.module.ts b/packages/twenty-server/src/modules/emailing/emailing.module.ts index 2a2cef5ad1..35e2afc716 100644 --- a/packages/twenty-server/src/modules/emailing/emailing.module.ts +++ b/packages/twenty-server/src/modules/emailing/emailing.module.ts @@ -10,6 +10,7 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature- import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity'; import { MessageChannelMetadataModule } from 'src/engine/metadata-modules/message-channel/message-channel-metadata.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; +import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; import { provideWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/provide-workspace-scoped-repository'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module'; @@ -18,6 +19,7 @@ import { EmailingSendResolver } from 'src/modules/emailing/resolvers/emailing-se import { UnsubscribeTopicResolver } from 'src/modules/emailing/resolvers/unsubscribe-topic.resolver'; import { EmailBillingService } from 'src/modules/emailing/services/email-billing.service'; import { EmailingDomainSenderService } from 'src/modules/emailing/services/emailing-domain-sender.service'; +import { MessageCampaignStatisticsService } from 'src/modules/emailing/services/message-campaign-statistics.service'; import { MessageCampaignService } from 'src/modules/emailing/services/message-campaign.service'; import { MessageSuppressionService } from 'src/modules/emailing/services/message-suppression.service'; import { UnsubscribeTopicService } from 'src/modules/emailing/services/unsubscribe-topic.service'; @@ -28,6 +30,7 @@ import { UnsubscribeTopicService } from 'src/modules/emailing/services/unsubscri MessageChannelMetadataModule, FeatureFlagModule, PermissionsModule, + UserRoleModule, BillingModule, WorkspaceEventEmitterModule, WorkspaceCacheModule, @@ -42,6 +45,7 @@ import { UnsubscribeTopicService } from 'src/modules/emailing/services/unsubscri providers: [ EmailBillingService, MessageCampaignService, + MessageCampaignStatisticsService, MessageSuppressionService, UnsubscribeTopicService, EmailingDomainSenderService, @@ -54,6 +58,7 @@ import { UnsubscribeTopicService } from 'src/modules/emailing/services/unsubscri exports: [ EmailingDomainSenderService, MessageCampaignService, + MessageCampaignStatisticsService, MessageSuppressionService, UnsubscribeTopicService, ], diff --git a/packages/twenty-server/src/modules/emailing/jobs/refresh-campaign-stats.job.ts b/packages/twenty-server/src/modules/emailing/jobs/refresh-campaign-stats.job.ts new file mode 100644 index 0000000000..f55a4d019b --- /dev/null +++ b/packages/twenty-server/src/modules/emailing/jobs/refresh-campaign-stats.job.ts @@ -0,0 +1,21 @@ +import { REFRESH_CAMPAIGN_STATS_JOB } from 'src/engine/core-modules/emailing-domain/constants/campaign.constant'; +import { type RefreshCampaignStatsJobData } from 'src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type'; +import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; +import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; +import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; +import { MessageCampaignStatisticsService } from 'src/modules/emailing/services/message-campaign-statistics.service'; + +@Processor(MessageQueue.emailQueue) +export class RefreshCampaignStatsJob { + constructor( + private readonly messageCampaignStatisticsService: MessageCampaignStatisticsService, + ) {} + + @Process(REFRESH_CAMPAIGN_STATS_JOB) + async handle(data: RefreshCampaignStatsJobData): Promise { + await this.messageCampaignStatisticsService.refreshCampaignCounts({ + workspaceId: data.workspaceId, + campaignId: data.campaignId, + }); + } +} diff --git a/packages/twenty-server/src/modules/emailing/resolvers/emailing-send.resolver.ts b/packages/twenty-server/src/modules/emailing/resolvers/emailing-send.resolver.ts index c8cfa5abe1..9b64cfe485 100644 --- a/packages/twenty-server/src/modules/emailing/resolvers/emailing-send.resolver.ts +++ b/packages/twenty-server/src/modules/emailing/resolvers/emailing-send.resolver.ts @@ -97,11 +97,13 @@ export class EmailingSendResolver { async previewMessageCampaignAudience( @Args('input') input: PreviewMessageCampaignAudienceInput, @AuthWorkspace() currentWorkspace: WorkspaceEntity, + @AuthUserWorkspaceId() userWorkspaceId: string, ): Promise { this.emailGroupAccessService.validateEmailGroupAccessOrThrow(); return this.messageCampaignService.previewAudience({ workspaceId: currentWorkspace.id, + userWorkspaceId, listId: input.listId, unsubscribeTopicId: input.unsubscribeTopicId, }); diff --git a/packages/twenty-server/src/modules/emailing/services/message-campaign-statistics.service.ts b/packages/twenty-server/src/modules/emailing/services/message-campaign-statistics.service.ts new file mode 100644 index 0000000000..3a6d41cad0 --- /dev/null +++ b/packages/twenty-server/src/modules/emailing/services/message-campaign-statistics.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; + +import { CAMPAIGN_MESSAGE_DELIVERY_STATUS } from 'src/engine/core-modules/emailing-domain/constants/campaign.constant'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; +import { MessageCampaignWorkspaceEntity } from 'src/modules/emailing/standard-objects/message-campaign.workspace-entity'; +import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity'; + +type DeliveryStatusCountRow = { + deliveryStatus: string | null; + count: string; +}; + +@Injectable() +export class MessageCampaignStatisticsService { + constructor( + private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, + ) {} + + async refreshCampaignCounts({ + workspaceId, + campaignId, + }: { + workspaceId: string; + campaignId: string; + }): Promise { + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + MessageWorkspaceEntity, + { shouldBypassPermissionChecks: true }, + ); + + const deliveryStatusCountRows = await messageRepository + .createQueryBuilder('message') + .select('message.deliveryStatus', 'deliveryStatus') + .addSelect('COUNT(*)', 'count') + .where('message.messageCampaignId = :campaignId', { campaignId }) + .groupBy('message.deliveryStatus') + .getRawMany(); + + const countByDeliveryStatus = new Map( + deliveryStatusCountRows.map((row) => [ + row.deliveryStatus, + Number(row.count), + ]), + ); + + const sentCount = + countByDeliveryStatus.get(CAMPAIGN_MESSAGE_DELIVERY_STATUS.SENT) ?? 0; + const failedCount = + countByDeliveryStatus.get(CAMPAIGN_MESSAGE_DELIVERY_STATUS.FAILED) ?? 0; + const bouncedCount = + countByDeliveryStatus.get(CAMPAIGN_MESSAGE_DELIVERY_STATUS.BOUNCED) ?? + 0; + const complainedCount = + countByDeliveryStatus.get( + CAMPAIGN_MESSAGE_DELIVERY_STATUS.COMPLAINED, + ) ?? 0; + + const campaignRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + MessageCampaignWorkspaceEntity, + { shouldBypassPermissionChecks: true }, + ); + + await campaignRepository.update( + { id: campaignId }, + { sentCount, failedCount, bouncedCount, complainedCount }, + ); + }, buildSystemAuthContext(workspaceId)); + } +} diff --git a/packages/twenty-server/src/modules/emailing/services/message-campaign.service.ts b/packages/twenty-server/src/modules/emailing/services/message-campaign.service.ts index f63cc17683..855e269d1b 100644 --- a/packages/twenty-server/src/modules/emailing/services/message-campaign.service.ts +++ b/packages/twenty-server/src/modules/emailing/services/message-campaign.service.ts @@ -7,9 +7,12 @@ import { v4, v5 } from 'uuid'; import { CAMPAIGN_MESSAGE_DELIVERY_STATUS, CAMPAIGN_MESSAGE_ID_NAMESPACE, + CAMPAIGN_STATS_REFRESH_DEBOUNCE_MS, + CAMPAIGN_STATS_REFRESH_DELAY_MS, CAMPAIGN_STATUS, MATERIALIZE_CAMPAIGN_JOB, MAX_CAMPAIGN_RECIPIENTS, + REFRESH_CAMPAIGN_STATS_JOB, SEND_CAMPAIGN_EMAIL_JOB, } from 'src/engine/core-modules/emailing-domain/constants/campaign.constant'; import { @@ -23,12 +26,17 @@ import { type CampaignRecipient } from 'src/engine/core-modules/emailing-domain/ import { type CampaignSkippedBreakdown } from 'src/engine/core-modules/emailing-domain/types/campaign-skipped-breakdown.type'; import { type MaterializeCampaignJobData } from 'src/engine/core-modules/emailing-domain/types/materialize-campaign-job-data.type'; import { type RawCampaignRecipient } from 'src/engine/core-modules/emailing-domain/types/raw-campaign-recipient.type'; +import { type RefreshCampaignStatsJobData } from 'src/engine/core-modules/emailing-domain/types/refresh-campaign-stats-job-data.type'; import { type SendCampaignEmailJobData } from 'src/engine/core-modules/emailing-domain/types/send-campaign-email-job-data.type'; import { normalizeCampaignRecipients } from 'src/engine/core-modules/emailing-domain/utils/normalize-campaign-recipients.util'; +import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator'; +import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service'; +import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageChannelMetadataService } from 'src/engine/metadata-modules/message-channel/message-channel-metadata.service'; +import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @@ -36,6 +44,7 @@ import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository'; import { EmailBillingService } from 'src/modules/emailing/services/email-billing.service'; import { EmailingDomainSenderService } from 'src/modules/emailing/services/emailing-domain-sender.service'; +import { MessageCampaignStatisticsService } from 'src/modules/emailing/services/message-campaign-statistics.service'; import { MessageSuppressionService } from 'src/modules/emailing/services/message-suppression.service'; import { MessageCampaignWorkspaceEntity } from 'src/modules/emailing/standard-objects/message-campaign.workspace-entity'; import { MessageListMemberWorkspaceEntity } from 'src/modules/emailing/standard-objects/message-list-member.workspace-entity'; @@ -99,14 +108,21 @@ export class MessageCampaignService { private readonly messageQueueService: MessageQueueService, private readonly messageChannelMetadataService: MessageChannelMetadataService, private readonly messageSuppressionService: MessageSuppressionService, + private readonly userRoleService: UserRoleService, + private readonly messageCampaignStatisticsService: MessageCampaignStatisticsService, private readonly emailBillingService: EmailBillingService, + @InjectCacheStorage(CacheStorageNamespace.ModuleEmailing) + private readonly cacheStorageService: CacheStorageService, ) {} private getUserRepository( workspaceId: string, entity: Type, + roleId: string, ) { - return this.globalWorkspaceOrmManager.getRepository(workspaceId, entity); + return this.globalWorkspaceOrmManager.getRepository(workspaceId, entity, { + unionOf: [roleId], + }); } private getSystemRepository( @@ -140,12 +156,18 @@ export class MessageCampaignService { ); } + const roleId = await this.userRoleService.getRoleIdForUserWorkspace({ + workspaceId, + userWorkspaceId, + }); + const { campaignId, recipients, skipped } = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( async () => { const rawRecipients = await this.resolveRecipientsFromList( workspaceId, listId, + roleId, ); const normalized = normalizeCampaignRecipients( @@ -155,6 +177,7 @@ export class MessageCampaignService { const newCampaignId = await this.createCampaign({ workspaceId, + roleId, subject, html, fromAddress, @@ -470,11 +493,17 @@ export class MessageCampaignService { } await messageRepository.update(message.id, { deliveryStatus }); + + await this.scheduleCampaignStatsRefresh({ + workspaceId, + campaignId: message.messageCampaignId, + }); }, buildSystemAuthContext(workspaceId)); } private async createCampaign({ workspaceId, + roleId, subject, html, fromAddress, @@ -482,6 +511,7 @@ export class MessageCampaignService { listId, }: { workspaceId: string; + roleId: string; subject: string; html: string; fromAddress: string; @@ -491,6 +521,7 @@ export class MessageCampaignService { const campaignRepository = await this.getUserRepository( workspaceId, MessageCampaignWorkspaceEntity, + roleId, ); const { identifiers } = await campaignRepository.insert({ @@ -654,22 +685,58 @@ export class MessageCampaignService { sentAt: new Date(), }, ); + + await this.scheduleCampaignStatsRefresh({ + workspaceId, + campaignId, + }); + } + + private async scheduleCampaignStatsRefresh({ + workspaceId, + campaignId, + }: { + workspaceId: string; + campaignId: string; + }): Promise { + const acquired = await this.cacheStorageService.acquireLock( + `campaign-stats-refresh:${workspaceId}:${campaignId}`, + CAMPAIGN_STATS_REFRESH_DEBOUNCE_MS, + ); + + if (!acquired) { + return; + } + + await this.messageQueueService.add( + REFRESH_CAMPAIGN_STATS_JOB, + { workspaceId, campaignId }, + { delay: CAMPAIGN_STATS_REFRESH_DELAY_MS }, + ); } async previewAudience({ workspaceId, + userWorkspaceId, listId, unsubscribeTopicId, }: { workspaceId: string; + userWorkspaceId: string; listId: string; unsubscribeTopicId?: string; }): Promise { + const roleId = await this.userRoleService.getRoleIdForUserWorkspace({ + workspaceId, + userWorkspaceId, + }); + return this.globalWorkspaceOrmManager.executeInWorkspaceContext( async () => { const rawRecipients = await this.resolveRecipientsFromList( workspaceId, listId, + roleId, ); const totalMembers = rawRecipients.length; @@ -724,10 +791,12 @@ export class MessageCampaignService { private async resolveRecipientsFromList( workspaceId: string, listId: string, + roleId: string, ): Promise { const listMemberRepository = await this.getUserRepository( workspaceId, MessageListMemberWorkspaceEntity, + roleId, ); const members = await listMemberRepository.find({ @@ -737,12 +806,14 @@ export class MessageCampaignService { return this.loadRecipientsByPersonIds( workspaceId, members.map((member) => member.personId), + roleId, ); } private async loadRecipientsByPersonIds( workspaceId: string, personIds: string[], + roleId: string, ): Promise { if (personIds.length === 0) { return []; @@ -751,6 +822,7 @@ export class MessageCampaignService { const personRepository = await this.getUserRepository( workspaceId, PersonWorkspaceEntity, + roleId, ); const people = await personRepository.find({ diff --git a/packages/twenty-server/src/modules/emailing/standard-objects/message-campaign.workspace-entity.ts b/packages/twenty-server/src/modules/emailing/standard-objects/message-campaign.workspace-entity.ts index 8045d660f0..ed05dafa91 100644 --- a/packages/twenty-server/src/modules/emailing/standard-objects/message-campaign.workspace-entity.ts +++ b/packages/twenty-server/src/modules/emailing/standard-objects/message-campaign.workspace-entity.ts @@ -12,6 +12,10 @@ export class MessageCampaignWorkspaceEntity extends BaseWorkspaceEntity { fromAddress: EmailsMetadata | null; status: string; sentAt: Date | null; + sentCount: number; + failedCount: number; + bouncedCount: number; + complainedCount: number; unsubscribeTopicId: string | null; list: EntityRelation | null; listId: string | null; diff --git a/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap b/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap index 6bb009378d..d8a22ced28 100644 --- a/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap +++ b/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap @@ -1055,6 +1055,12 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "bodyTemplate": { "universalIdentifier": "b3a69d08-31ca-4a8d-8359-5ca462899342", }, + "bouncedCount": { + "universalIdentifier": "20d884a9-34dd-4667-8ecb-ceec224258e2", + }, + "complainedCount": { + "universalIdentifier": "82842cfa-f12a-4bab-bbde-b2cf587d0406", + }, "createdAt": { "universalIdentifier": "4260eb89-0d55-5078-a6e5-5768781111ba", }, @@ -1064,6 +1070,9 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "deletedAt": { "universalIdentifier": "da77cebd-4e72-5568-b808-027e21f748e8", }, + "failedCount": { + "universalIdentifier": "d373fcd7-b1ce-4c77-8031-a5785c475028", + }, "fromAddress": { "universalIdentifier": "91e1a33c-c1ff-411a-b720-9085e13c05db", }, @@ -1088,6 +1097,9 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "sentAt": { "universalIdentifier": "e2315b4f-9edf-4df2-96b9-961e76368671", }, + "sentCount": { + "universalIdentifier": "2f333d2b-37b8-4ddc-ad0d-c07c6ce066ad", + }, "status": { "universalIdentifier": "c7117256-3de6-48e1-87df-c99c32bad610", }, @@ -1119,6 +1131,46 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e }, }, "universalIdentifier": "238acb94-dd4c-4036-bc55-19b99d821efd", + "views": { + "allMessageCampaigns": { + "universalIdentifier": "ffedb368-33f0-43a7-b84e-e622b4e97be9", + "viewFields": { + "bouncedCount": { + "universalIdentifier": "135d3fb9-bc45-4426-980b-9836ed792e3a", + }, + "complainedCount": { + "universalIdentifier": "9957dc6d-05ae-4829-ae7d-7cdf9239cdd0", + }, + "createdAt": { + "universalIdentifier": "6dffa47d-3128-4d54-924a-4f67676116c2", + }, + "failedCount": { + "universalIdentifier": "ad346c8e-f462-499e-89c8-da01d5460dc6", + }, + "fromAddress": { + "universalIdentifier": "553a0ea6-1f55-4143-a2d0-8c1ad0cf3dd1", + }, + "list": { + "universalIdentifier": "c0bf6a48-8695-4520-81e6-7462d35033b5", + }, + "recipients": { + "universalIdentifier": "2f99f444-4af3-44fd-b08d-46955c4ac2a2", + }, + "sentAt": { + "universalIdentifier": "7f4a9d81-afac-4d27-98de-0a2da65114f7", + }, + "sentCount": { + "universalIdentifier": "0be62e57-3955-49bd-a9ef-8f82f7a6e9aa", + }, + "status": { + "universalIdentifier": "ef35df97-b1a5-4f16-8b85-5751e5019f63", + }, + "subject": { + "universalIdentifier": "b017c851-e38d-4a81-ab38-0fb10e9f239e", + }, + }, + }, + }, }, "messageChannelMessageAssociation": { "fields": { @@ -1366,6 +1418,25 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e }, }, "universalIdentifier": "826561ea-4816-411c-baa0-eec5e6ca8866", + "views": { + "allMessageLists": { + "universalIdentifier": "c72bae18-75e9-4cb0-baeb-379d3529b98f", + "viewFields": { + "campaigns": { + "universalIdentifier": "037030a2-9dad-4bfd-957a-313b362172b4", + }, + "createdAt": { + "universalIdentifier": "29f3e7de-c40b-4597-8568-7318c146e4da", + }, + "members": { + "universalIdentifier": "6d314c4b-215a-4094-963a-ff9dd8221aea", + }, + "name": { + "universalIdentifier": "c9767580-34e6-420b-923d-b4abd8c13d96", + }, + }, + }, + }, }, "messageListMember": { "fields": { diff --git a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts index 67c8bbea64..cea91cea74 100644 --- a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts +++ b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts @@ -790,6 +790,18 @@ export const STANDARD_OBJECTS = { }, status: { universalIdentifier: 'c7117256-3de6-48e1-87df-c99c32bad610' }, sentAt: { universalIdentifier: 'e2315b4f-9edf-4df2-96b9-961e76368671' }, + sentCount: { + universalIdentifier: '2f333d2b-37b8-4ddc-ad0d-c07c6ce066ad', + }, + failedCount: { + universalIdentifier: 'd373fcd7-b1ce-4c77-8031-a5785c475028', + }, + bouncedCount: { + universalIdentifier: '20d884a9-34dd-4667-8ecb-ceec224258e2', + }, + complainedCount: { + universalIdentifier: '82842cfa-f12a-4bab-bbde-b2cf587d0406', + }, unsubscribeTopicId: { universalIdentifier: '0648e7ad-1769-4ff6-a4d5-72da79ef169c', }, @@ -813,6 +825,46 @@ export const STANDARD_OBJECTS = { universalIdentifier: '975823ad-9b97-4f39-b2c7-fbd7d77f4bd1', }, }, + views: { + allMessageCampaigns: { + universalIdentifier: 'ffedb368-33f0-43a7-b84e-e622b4e97be9', + viewFields: { + subject: { + universalIdentifier: 'b017c851-e38d-4a81-ab38-0fb10e9f239e', + }, + status: { + universalIdentifier: 'ef35df97-b1a5-4f16-8b85-5751e5019f63', + }, + list: { + universalIdentifier: 'c0bf6a48-8695-4520-81e6-7462d35033b5', + }, + fromAddress: { + universalIdentifier: '553a0ea6-1f55-4143-a2d0-8c1ad0cf3dd1', + }, + sentAt: { + universalIdentifier: '7f4a9d81-afac-4d27-98de-0a2da65114f7', + }, + sentCount: { + universalIdentifier: '0be62e57-3955-49bd-a9ef-8f82f7a6e9aa', + }, + failedCount: { + universalIdentifier: 'ad346c8e-f462-499e-89c8-da01d5460dc6', + }, + bouncedCount: { + universalIdentifier: '135d3fb9-bc45-4426-980b-9836ed792e3a', + }, + complainedCount: { + universalIdentifier: '9957dc6d-05ae-4829-ae7d-7cdf9239cdd0', + }, + recipients: { + universalIdentifier: '2f99f444-4af3-44fd-b08d-46955c4ac2a2', + }, + createdAt: { + universalIdentifier: '6dffa47d-3128-4d54-924a-4f67676116c2', + }, + }, + }, + }, }, messageList: { universalIdentifier: '826561ea-4816-411c-baa0-eec5e6ca8866', @@ -836,6 +888,25 @@ export const STANDARD_OBJECTS = { universalIdentifier: '8e205171-ed74-4620-b7d2-674aab85033a', }, }, + views: { + allMessageLists: { + universalIdentifier: 'c72bae18-75e9-4cb0-baeb-379d3529b98f', + viewFields: { + name: { + universalIdentifier: 'c9767580-34e6-420b-923d-b4abd8c13d96', + }, + members: { + universalIdentifier: '6d314c4b-215a-4094-963a-ff9dd8221aea', + }, + campaigns: { + universalIdentifier: '037030a2-9dad-4bfd-957a-313b362172b4', + }, + createdAt: { + universalIdentifier: '29f3e7de-c40b-4597-8568-7318c146e4da', + }, + }, + }, + }, }, messageListMember: { universalIdentifier: '27773d24-8ce3-40f8-aa6c-1f590f2c08d2',