From d54b7132640ebc989f8c856f331e3f88f8df29cf Mon Sep 17 00:00:00 2001 From: neo773 <62795688+neo773@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:50:37 +0530 Subject: [PATCH] Respect Gmail retry-after in messaging throttle (#17850) Gmail 429/403 rate-limit responses include an explicit retry-after timestamp, usually ~15 minutes out. The exponential backoff starts at 1 minute, so the channel burns through all 5 retry attempts before the window actually closes and gets marked as permanently failed. Adds throttleRetryAfter to the message channel and uses max(backoff, retryAfter) in isThrottled(). --- ...ge-channel-throttle-retry-after.command.ts | 120 ++ .../1-18-upgrade-version-command.module.ts | 3 + .../upgrade.command.ts | 3 + ...adata-related-entity-ids.util.spec.ts.snap | 1723 +++++++++-------- ...annel-standard-flat-field-metadata.util.ts | 17 + .../utils/__tests__/is-throttled.spec.ts | 52 + .../connected-account/utils/is-throttled.ts | 25 +- .../message-channel-sync-status.service.ts | 3 + .../message-channel.workspace-entity.ts | 1 + .../message-import-driver.exception.ts | 3 + .../gmail/mocks/gmail-api-error-mocks.ts | 10 +- .../parse-gmail-error-retry-after.spec.ts | 40 + ...rse-gmail-message-list-fetch-error.spec.ts | 44 + .../gmail/utils/parse-gmail-api-error.util.ts | 9 + .../parse-gmail-error-retry-after.util.ts | 24 + .../jobs/messaging-message-list-fetch.job.ts | 1 + .../jobs/messaging-messages-import.job.ts | 1 + .../services/messaging-cursor.service.ts | 2 + ...saging-import-exception-handler.service.ts | 16 + .../messaging-messages-import.service.ts | 1 + .../src/metadata/standard-object.constant.ts | 3 + 21 files changed, 1235 insertions(+), 866 deletions(-) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command.ts create mode 100644 packages/twenty-server/src/modules/connected-account/utils/__tests__/is-throttled.spec.ts create mode 100644 packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-error-retry-after.spec.ts create mode 100644 packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command.ts new file mode 100644 index 0000000000..b1b888b124 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command.ts @@ -0,0 +1,120 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/services/application.service'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; +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 { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; + +@Command({ + name: 'upgrade:1-18:backfill-message-channel-throttle-retry-after', + description: + 'Backfill throttleRetryAfter field on messageChannel standard object', +}) +export class BackfillMessageChannelThrottleRetryAfterCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + private readonly applicationService: ApplicationService, + private readonly fieldMetadataService: FieldMetadataService, + private readonly workspaceCacheService: WorkspaceCacheService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Backfilling throttleRetryAfter field for workspace ${workspaceId}`, + ); + + const { flatObjectMetadataMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatObjectMetadataMaps', + ]); + + const messageChannelObjectMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatObjectMetadataMaps, + universalIdentifier: + STANDARD_OBJECTS.messageChannel.universalIdentifier, + }); + + if (!messageChannelObjectMetadata) { + this.logger.log( + `MessageChannel object metadata not found for workspace ${workspaceId}. Skipping.`, + ); + + return; + } + + const { flatFieldMetadataMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatFieldMetadataMaps', + ]); + + const existingField = findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatFieldMetadataMaps, + universalIdentifier: + STANDARD_OBJECTS.messageChannel.fields.throttleRetryAfter + .universalIdentifier, + }); + + if (existingField) { + this.logger.log( + `throttleRetryAfter field already exists for workspace ${workspaceId}. Skipping.`, + ); + + return; + } + + if (options.dryRun) { + this.logger.log( + `Would have created throttleRetryAfter field for workspace ${workspaceId}. Skipping (dry run).`, + ); + + return; + } + + const { twentyStandardFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + await this.fieldMetadataService.createOneField({ + createFieldInput: { + name: 'throttleRetryAfter', + type: FieldMetadataType.DATE_TIME, + label: 'Throttle Retry After', + description: 'Throttle Retry After', + icon: 'IconClock', + isNullable: true, + isUIReadOnly: true, + objectMetadataId: messageChannelObjectMetadata.id, + universalIdentifier: + STANDARD_OBJECTS.messageChannel.fields.throttleRetryAfter + .universalIdentifier, + }, + workspaceId, + ownerFlatApplication: twentyStandardFlatApplication, + }); + + this.logger.log( + `Successfully created throttleRetryAfter field for workspace ${workspaceId}`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts index 382a53160b..d41d68a7fe 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BackfillFileSizeAndMimeTypeCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-file-size-and-mime-type.command'; +import { BackfillMessageChannelThrottleRetryAfterCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command'; import { MigrateActivityRichTextAttachmentFileIdsCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command'; import { MigrateAttachmentFilesCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command'; import { MigratePersonAvatarFilesCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command'; @@ -40,12 +41,14 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso MigrateAttachmentFilesCommand, BackfillFileSizeAndMimeTypeCommand, MigrateActivityRichTextAttachmentFileIdsCommand, + BackfillMessageChannelThrottleRetryAfterCommand, ], exports: [ MigratePersonAvatarFilesCommand, MigrateAttachmentFilesCommand, BackfillFileSizeAndMimeTypeCommand, MigrateActivityRichTextAttachmentFileIdsCommand, + BackfillMessageChannelThrottleRetryAfterCommand, ], }) export class V1_18_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index 1c99e875d8..b3471c2f2d 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -20,6 +20,7 @@ import { MigrateNoteTargetToMorphRelationsCommand } from 'src/database/commands/ import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command'; import { MigrateWorkflowCodeStepsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-workflow-code-steps.command'; import { BackfillFileSizeAndMimeTypeCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-file-size-and-mime-type.command'; +import { BackfillMessageChannelThrottleRetryAfterCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command'; import { MigrateActivityRichTextAttachmentFileIdsCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command'; import { MigrateAttachmentFilesCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command'; import { MigratePersonAvatarFilesCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command'; @@ -59,6 +60,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly backfillFileSizeAndMimeTypeCommand: BackfillFileSizeAndMimeTypeCommand, protected readonly migrateAttachmentFilesCommand: MigrateAttachmentFilesCommand, protected readonly migrateActivityRichTextAttachmentFileIdsCommand: MigrateActivityRichTextAttachmentFileIdsCommand, + protected readonly backfillMessageChannelThrottleRetryAfterCommand: BackfillMessageChannelThrottleRetryAfterCommand, ) { super( workspaceRepository, @@ -89,6 +91,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.migrateAttachmentFilesCommand, this.migrateActivityRichTextAttachmentFileIdsCommand, this.backfillFileSizeAndMimeTypeCommand, + this.backfillMessageChannelThrottleRetryAfterCommand, ]; this.allCommands = { 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 995c639d83..3c6271820a 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 @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`getStandardObjectMetadataRelatedEntityIds should return standard object metadata related entity ids 1`] = ` { @@ -667,68 +667,68 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "message": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000274", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000276", - }, - "direction": { - "id": "00000000-0000-0000-0000-000000000279", - }, - "headerMessageId": { - "id": "00000000-0000-0000-0000-000000000277", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000273", - }, - "messageChannelMessageAssociations": { - "id": "00000000-0000-0000-0000-000000000284", - }, - "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000283", - }, - "messageThread": { - "id": "00000000-0000-0000-0000-000000000278", - }, - "receivedAt": { - "id": "00000000-0000-0000-0000-000000000282", - }, - "subject": { - "id": "00000000-0000-0000-0000-000000000280", - }, - "text": { - "id": "00000000-0000-0000-0000-000000000281", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000275", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000277", + }, + "direction": { + "id": "00000000-0000-0000-0000-000000000280", + }, + "headerMessageId": { + "id": "00000000-0000-0000-0000-000000000278", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000274", + }, + "messageChannelMessageAssociations": { + "id": "00000000-0000-0000-0000-000000000285", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000284", + }, + "messageThread": { + "id": "00000000-0000-0000-0000-000000000279", + }, + "receivedAt": { + "id": "00000000-0000-0000-0000-000000000283", + }, + "subject": { + "id": "00000000-0000-0000-0000-000000000281", + }, + "text": { + "id": "00000000-0000-0000-0000-000000000282", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000276", + }, }, - "id": "00000000-0000-0000-0000-000000000293", + "id": "00000000-0000-0000-0000-000000000294", "views": { "allMessages": { - "id": "00000000-0000-0000-0000-000000000292", + "id": "00000000-0000-0000-0000-000000000293", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000291", + "id": "00000000-0000-0000-0000-000000000292", }, "headerMessageId": { - "id": "00000000-0000-0000-0000-000000000289", + "id": "00000000-0000-0000-0000-000000000290", }, "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000287", - }, - "messageThread": { - "id": "00000000-0000-0000-0000-000000000286", - }, - "receivedAt": { "id": "00000000-0000-0000-0000-000000000288", }, + "messageThread": { + "id": "00000000-0000-0000-0000-000000000287", + }, + "receivedAt": { + "id": "00000000-0000-0000-0000-000000000289", + }, "subject": { - "id": "00000000-0000-0000-0000-000000000285", + "id": "00000000-0000-0000-0000-000000000286", }, "text": { - "id": "00000000-0000-0000-0000-000000000290", + "id": "00000000-0000-0000-0000-000000000291", }, }, "viewGroups": {}, @@ -797,6 +797,9 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "throttleFailureCount": { "id": "00000000-0000-0000-0000-000000000237", }, + "throttleRetryAfter": { + "id": "00000000-0000-0000-0000-000000000238", + }, "type": { "id": "00000000-0000-0000-0000-000000000222", }, @@ -807,7 +810,7 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000219", }, }, - "id": "00000000-0000-0000-0000-000000000238", + "id": "00000000-0000-0000-0000-000000000239", "views": undefined, }, "messageChannelMessageAssociation": { @@ -849,113 +852,113 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "messageFolder": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000240", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000242", - }, - "externalId": { - "id": "00000000-0000-0000-0000-000000000249", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000239", - }, - "isSentFolder": { - "id": "00000000-0000-0000-0000-000000000247", - }, - "isSynced": { - "id": "00000000-0000-0000-0000-000000000248", - }, - "messageChannel": { - "id": "00000000-0000-0000-0000-000000000245", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000243", - }, - "parentFolderId": { - "id": "00000000-0000-0000-0000-000000000244", - }, - "pendingSyncAction": { - "id": "00000000-0000-0000-0000-000000000250", - }, - "syncCursor": { - "id": "00000000-0000-0000-0000-000000000246", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000241", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000243", + }, + "externalId": { + "id": "00000000-0000-0000-0000-000000000250", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000240", + }, + "isSentFolder": { + "id": "00000000-0000-0000-0000-000000000248", + }, + "isSynced": { + "id": "00000000-0000-0000-0000-000000000249", + }, + "messageChannel": { + "id": "00000000-0000-0000-0000-000000000246", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000244", + }, + "parentFolderId": { + "id": "00000000-0000-0000-0000-000000000245", + }, + "pendingSyncAction": { + "id": "00000000-0000-0000-0000-000000000251", + }, + "syncCursor": { + "id": "00000000-0000-0000-0000-000000000247", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000242", + }, }, - "id": "00000000-0000-0000-0000-000000000251", + "id": "00000000-0000-0000-0000-000000000252", "views": undefined, }, "messageParticipant": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000253", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000255", - }, - "displayName": { - "id": "00000000-0000-0000-0000-000000000259", - }, - "handle": { - "id": "00000000-0000-0000-0000-000000000258", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000252", - }, - "message": { - "id": "00000000-0000-0000-0000-000000000256", - }, - "person": { - "id": "00000000-0000-0000-0000-000000000260", - }, - "role": { - "id": "00000000-0000-0000-0000-000000000257", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000254", }, - "workspaceMember": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000256", + }, + "displayName": { + "id": "00000000-0000-0000-0000-000000000260", + }, + "handle": { + "id": "00000000-0000-0000-0000-000000000259", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000253", + }, + "message": { + "id": "00000000-0000-0000-0000-000000000257", + }, + "person": { "id": "00000000-0000-0000-0000-000000000261", }, + "role": { + "id": "00000000-0000-0000-0000-000000000258", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000255", + }, + "workspaceMember": { + "id": "00000000-0000-0000-0000-000000000262", + }, }, - "id": "00000000-0000-0000-0000-000000000262", + "id": "00000000-0000-0000-0000-000000000263", "views": undefined, }, "messageThread": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000264", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000266", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000263", - }, - "messageChannelMessageAssociations": { - "id": "00000000-0000-0000-0000-000000000268", - }, - "messages": { - "id": "00000000-0000-0000-0000-000000000267", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000265", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000267", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000264", + }, + "messageChannelMessageAssociations": { + "id": "00000000-0000-0000-0000-000000000269", + }, + "messages": { + "id": "00000000-0000-0000-0000-000000000268", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000266", + }, }, - "id": "00000000-0000-0000-0000-000000000272", + "id": "00000000-0000-0000-0000-000000000273", "views": { "allMessageThreads": { - "id": "00000000-0000-0000-0000-000000000271", + "id": "00000000-0000-0000-0000-000000000272", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000270", + "id": "00000000-0000-0000-0000-000000000271", }, "messages": { - "id": "00000000-0000-0000-0000-000000000269", + "id": "00000000-0000-0000-0000-000000000270", }, }, "viewGroups": {}, @@ -965,94 +968,94 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "note": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000304", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000300", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000295", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000301", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000297", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000306", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000294", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000303", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000298", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000307", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000305", }, - "title": { - "id": "00000000-0000-0000-0000-000000000299", + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000301", }, - "updatedAt": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000296", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000302", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000298", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000307", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000295", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000304", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000299", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000308", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000306", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000300", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000297", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000303", + }, }, - "id": "00000000-0000-0000-0000-000000000321", + "id": "00000000-0000-0000-0000-000000000322", "views": { "allNotes": { - "id": "00000000-0000-0000-0000-000000000313", + "id": "00000000-0000-0000-0000-000000000314", "viewFieldGroups": {}, "viewFields": { "bodyV2": { - "id": "00000000-0000-0000-0000-000000000310", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000312", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000311", }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000313", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000312", + }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000309", + "id": "00000000-0000-0000-0000-000000000310", }, "title": { - "id": "00000000-0000-0000-0000-000000000308", + "id": "00000000-0000-0000-0000-000000000309", }, }, "viewGroups": {}, }, "noteRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000320", + "id": "00000000-0000-0000-0000-000000000321", "viewFieldGroups": { "additional": { - "id": "00000000-0000-0000-0000-000000000318", + "id": "00000000-0000-0000-0000-000000000319", }, "general": { - "id": "00000000-0000-0000-0000-000000000317", + "id": "00000000-0000-0000-0000-000000000318", }, "other": { - "id": "00000000-0000-0000-0000-000000000319", + "id": "00000000-0000-0000-0000-000000000320", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000315", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000316", }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000317", + }, "title": { - "id": "00000000-0000-0000-0000-000000000314", + "id": "00000000-0000-0000-0000-000000000315", }, }, "viewGroups": {}, @@ -1062,201 +1065,201 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "noteTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000323", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000325", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000322", - }, - "note": { - "id": "00000000-0000-0000-0000-000000000326", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000328", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000329", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000327", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000324", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000326", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000323", + }, + "note": { + "id": "00000000-0000-0000-0000-000000000327", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000329", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000330", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000328", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000325", + }, }, - "id": "00000000-0000-0000-0000-000000000330", + "id": "00000000-0000-0000-0000-000000000331", "views": undefined, }, "opportunity": { "fields": { "amount": { - "id": "00000000-0000-0000-0000-000000000336", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000348", - }, - "closeDate": { "id": "00000000-0000-0000-0000-000000000337", }, - "company": { - "id": "00000000-0000-0000-0000-000000000343", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000332", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000340", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000334", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000345", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000331", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000335", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000347", - }, - "owner": { - "id": "00000000-0000-0000-0000-000000000344", - }, - "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000342", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000339", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000350", - }, - "stage": { - "id": "00000000-0000-0000-0000-000000000338", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000346", - }, - "timelineActivities": { + "attachments": { "id": "00000000-0000-0000-0000-000000000349", }, - "updatedAt": { + "closeDate": { + "id": "00000000-0000-0000-0000-000000000338", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000344", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000333", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000341", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000335", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000346", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000332", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000336", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000348", + }, + "owner": { + "id": "00000000-0000-0000-0000-000000000345", + }, + "pointOfContact": { + "id": "00000000-0000-0000-0000-000000000343", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000340", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000351", + }, + "stage": { + "id": "00000000-0000-0000-0000-000000000339", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000347", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000350", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000334", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000342", + }, }, - "id": "00000000-0000-0000-0000-000000000382", + "id": "00000000-0000-0000-0000-000000000383", "views": { "allOpportunities": { - "id": "00000000-0000-0000-0000-000000000357", + "id": "00000000-0000-0000-0000-000000000358", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000352", - }, - "closeDate": { - "id": "00000000-0000-0000-0000-000000000354", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000355", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000353", }, + "closeDate": { + "id": "00000000-0000-0000-0000-000000000355", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000356", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000354", + }, "name": { - "id": "00000000-0000-0000-0000-000000000351", + "id": "00000000-0000-0000-0000-000000000352", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000356", + "id": "00000000-0000-0000-0000-000000000357", }, }, "viewGroups": {}, }, "byStage": { - "id": "00000000-0000-0000-0000-000000000369", + "id": "00000000-0000-0000-0000-000000000370", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000359", - }, - "closeDate": { - "id": "00000000-0000-0000-0000-000000000361", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000362", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000360", }, + "closeDate": { + "id": "00000000-0000-0000-0000-000000000362", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000363", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000361", + }, "name": { - "id": "00000000-0000-0000-0000-000000000358", + "id": "00000000-0000-0000-0000-000000000359", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000363", + "id": "00000000-0000-0000-0000-000000000364", }, }, "viewGroups": { "customer": { - "id": "00000000-0000-0000-0000-000000000368", + "id": "00000000-0000-0000-0000-000000000369", }, "meeting": { - "id": "00000000-0000-0000-0000-000000000366", - }, - "new": { - "id": "00000000-0000-0000-0000-000000000364", - }, - "proposal": { "id": "00000000-0000-0000-0000-000000000367", }, - "screening": { + "new": { "id": "00000000-0000-0000-0000-000000000365", }, + "proposal": { + "id": "00000000-0000-0000-0000-000000000368", + }, + "screening": { + "id": "00000000-0000-0000-0000-000000000366", + }, }, }, "opportunityRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000381", + "id": "00000000-0000-0000-0000-000000000382", "viewFieldGroups": { "additional": { - "id": "00000000-0000-0000-0000-000000000379", + "id": "00000000-0000-0000-0000-000000000380", }, "general": { - "id": "00000000-0000-0000-0000-000000000378", + "id": "00000000-0000-0000-0000-000000000379", }, "other": { - "id": "00000000-0000-0000-0000-000000000380", + "id": "00000000-0000-0000-0000-000000000381", }, }, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000370", - }, - "closeDate": { "id": "00000000-0000-0000-0000-000000000371", }, + "closeDate": { + "id": "00000000-0000-0000-0000-000000000372", + }, "company": { - "id": "00000000-0000-0000-0000-000000000373", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000376", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000377", - }, - "owner": { - "id": "00000000-0000-0000-0000-000000000375", - }, - "pointOfContact": { "id": "00000000-0000-0000-0000-000000000374", }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000377", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000378", + }, + "owner": { + "id": "00000000-0000-0000-0000-000000000376", + }, + "pointOfContact": { + "id": "00000000-0000-0000-0000-000000000375", + }, "stage": { - "id": "00000000-0000-0000-0000-000000000372", + "id": "00000000-0000-0000-0000-000000000373", }, }, "viewGroups": {}, @@ -1266,167 +1269,167 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "person": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000404", - }, - "avatarFile": { - "id": "00000000-0000-0000-0000-000000000395", - }, - "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000394", - }, - "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000406", - }, - "city": { - "id": "00000000-0000-0000-0000-000000000393", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000399", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000384", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000397", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000386", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000388", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000403", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000383", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000391", - }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000389", - }, - "messageParticipants": { "id": "00000000-0000-0000-0000-000000000405", }, - "name": { - "id": "00000000-0000-0000-0000-000000000387", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000402", - }, - "phones": { - "id": "00000000-0000-0000-0000-000000000392", - }, - "pointOfContactForOpportunities": { - "id": "00000000-0000-0000-0000-000000000400", - }, - "position": { + "avatarFile": { "id": "00000000-0000-0000-0000-000000000396", }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000408", + "avatarUrl": { + "id": "00000000-0000-0000-0000-000000000395", }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000401", - }, - "timelineActivities": { + "calendarEventParticipants": { "id": "00000000-0000-0000-0000-000000000407", }, - "updatedAt": { + "city": { + "id": "00000000-0000-0000-0000-000000000394", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000400", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000385", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000398", }, - "xLink": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000387", + }, + "emails": { + "id": "00000000-0000-0000-0000-000000000389", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000404", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000384", + }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000392", + }, + "linkedinLink": { "id": "00000000-0000-0000-0000-000000000390", }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000406", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000388", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000403", + }, + "phones": { + "id": "00000000-0000-0000-0000-000000000393", + }, + "pointOfContactForOpportunities": { + "id": "00000000-0000-0000-0000-000000000401", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000397", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000409", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000402", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000408", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000386", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000399", + }, + "xLink": { + "id": "00000000-0000-0000-0000-000000000391", + }, }, - "id": "00000000-0000-0000-0000-000000000434", + "id": "00000000-0000-0000-0000-000000000435", "views": { "allPeople": { - "id": "00000000-0000-0000-0000-000000000419", + "id": "00000000-0000-0000-0000-000000000420", "viewFieldGroups": {}, "viewFields": { "city": { - "id": "00000000-0000-0000-0000-000000000415", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000412", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000414", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000411", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000410", - }, - "jobTitle": { "id": "00000000-0000-0000-0000-000000000416", }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000417", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000409", - }, - "phones": { + "company": { "id": "00000000-0000-0000-0000-000000000413", }, - "xLink": { + "createdAt": { + "id": "00000000-0000-0000-0000-000000000415", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000412", + }, + "emails": { + "id": "00000000-0000-0000-0000-000000000411", + }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000417", + }, + "linkedinLink": { "id": "00000000-0000-0000-0000-000000000418", }, + "name": { + "id": "00000000-0000-0000-0000-000000000410", + }, + "phones": { + "id": "00000000-0000-0000-0000-000000000414", + }, + "xLink": { + "id": "00000000-0000-0000-0000-000000000419", + }, }, "viewGroups": {}, }, "personRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000433", + "id": "00000000-0000-0000-0000-000000000434", "viewFieldGroups": { "additional": { - "id": "00000000-0000-0000-0000-000000000431", + "id": "00000000-0000-0000-0000-000000000432", }, "general": { - "id": "00000000-0000-0000-0000-000000000430", + "id": "00000000-0000-0000-0000-000000000431", }, "other": { - "id": "00000000-0000-0000-0000-000000000432", + "id": "00000000-0000-0000-0000-000000000433", }, }, "viewFields": { "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000427", - }, - "city": { - "id": "00000000-0000-0000-0000-000000000426", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000422", - }, - "createdAt": { "id": "00000000-0000-0000-0000-000000000428", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000429", + "city": { + "id": "00000000-0000-0000-0000-000000000427", }, - "emails": { - "id": "00000000-0000-0000-0000-000000000420", - }, - "jobTitle": { + "company": { "id": "00000000-0000-0000-0000-000000000423", }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000424", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000429", }, - "phones": { + "createdBy": { + "id": "00000000-0000-0000-0000-000000000430", + }, + "emails": { "id": "00000000-0000-0000-0000-000000000421", }, - "xLink": { + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000424", + }, + "linkedinLink": { "id": "00000000-0000-0000-0000-000000000425", }, + "phones": { + "id": "00000000-0000-0000-0000-000000000422", + }, + "xLink": { + "id": "00000000-0000-0000-0000-000000000426", + }, }, "viewGroups": {}, }, @@ -1435,194 +1438,194 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "task": { "fields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000448", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000447", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000441", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000436", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000444", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000438", - }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000442", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000450", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000435", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000439", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000451", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000443", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000446", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000449", }, - "title": { - "id": "00000000-0000-0000-0000-000000000440", + "attachments": { + "id": "00000000-0000-0000-0000-000000000448", }, - "updatedAt": { + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000442", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000437", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000445", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000439", + }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000443", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000451", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000436", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000440", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000452", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000444", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000447", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000450", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000441", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000438", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000446", + }, }, - "id": "00000000-0000-0000-0000-000000000492", + "id": "00000000-0000-0000-0000-000000000493", "views": { "allTasks": { - "id": "00000000-0000-0000-0000-000000000460", + "id": "00000000-0000-0000-0000-000000000461", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000457", - }, - "bodyV2": { "id": "00000000-0000-0000-0000-000000000458", }, - "createdAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000459", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000455", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000460", }, - "dueAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000456", }, - "status": { - "id": "00000000-0000-0000-0000-000000000453", + "dueAt": { + "id": "00000000-0000-0000-0000-000000000457", }, - "taskTargets": { + "status": { "id": "00000000-0000-0000-0000-000000000454", }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000455", + }, "title": { - "id": "00000000-0000-0000-0000-000000000452", + "id": "00000000-0000-0000-0000-000000000453", }, }, "viewGroups": {}, }, "assignedToMe": { - "id": "00000000-0000-0000-0000-000000000472", + "id": "00000000-0000-0000-0000-000000000473", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000465", - }, - "bodyV2": { "id": "00000000-0000-0000-0000-000000000466", }, - "createdAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000467", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000463", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000468", }, - "dueAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000464", }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000465", + }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000462", + "id": "00000000-0000-0000-0000-000000000463", }, "title": { - "id": "00000000-0000-0000-0000-000000000461", + "id": "00000000-0000-0000-0000-000000000462", }, }, "viewGroups": { "done": { - "id": "00000000-0000-0000-0000-000000000470", - }, - "empty": { "id": "00000000-0000-0000-0000-000000000471", }, + "empty": { + "id": "00000000-0000-0000-0000-000000000472", + }, "inProgress": { - "id": "00000000-0000-0000-0000-000000000469", + "id": "00000000-0000-0000-0000-000000000470", }, "todo": { - "id": "00000000-0000-0000-0000-000000000468", + "id": "00000000-0000-0000-0000-000000000469", }, }, }, "byStatus": { - "id": "00000000-0000-0000-0000-000000000481", + "id": "00000000-0000-0000-0000-000000000482", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000476", - }, - "createdAt": { "id": "00000000-0000-0000-0000-000000000477", }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000478", + }, "dueAt": { - "id": "00000000-0000-0000-0000-000000000475", + "id": "00000000-0000-0000-0000-000000000476", }, "status": { - "id": "00000000-0000-0000-0000-000000000474", + "id": "00000000-0000-0000-0000-000000000475", }, "title": { - "id": "00000000-0000-0000-0000-000000000473", + "id": "00000000-0000-0000-0000-000000000474", }, }, "viewGroups": { "done": { - "id": "00000000-0000-0000-0000-000000000480", + "id": "00000000-0000-0000-0000-000000000481", }, "inProgress": { - "id": "00000000-0000-0000-0000-000000000479", + "id": "00000000-0000-0000-0000-000000000480", }, "todo": { - "id": "00000000-0000-0000-0000-000000000478", + "id": "00000000-0000-0000-0000-000000000479", }, }, }, "taskRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000491", + "id": "00000000-0000-0000-0000-000000000492", "viewFieldGroups": { "additional": { - "id": "00000000-0000-0000-0000-000000000489", + "id": "00000000-0000-0000-0000-000000000490", }, "general": { - "id": "00000000-0000-0000-0000-000000000488", + "id": "00000000-0000-0000-0000-000000000489", }, "other": { - "id": "00000000-0000-0000-0000-000000000490", + "id": "00000000-0000-0000-0000-000000000491", }, }, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000485", - }, - "createdAt": { "id": "00000000-0000-0000-0000-000000000486", }, - "createdBy": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000487", }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000483", + "createdBy": { + "id": "00000000-0000-0000-0000-000000000488", }, - "status": { + "dueAt": { "id": "00000000-0000-0000-0000-000000000484", }, + "status": { + "id": "00000000-0000-0000-0000-000000000485", + }, "title": { - "id": "00000000-0000-0000-0000-000000000482", + "id": "00000000-0000-0000-0000-000000000483", }, }, "viewGroups": {}, @@ -1632,117 +1635,117 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "taskTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000494", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000496", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000493", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000499", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000500", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000498", - }, - "task": { - "id": "00000000-0000-0000-0000-000000000497", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000495", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000497", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000494", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000500", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000501", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000499", + }, + "task": { + "id": "00000000-0000-0000-0000-000000000498", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000496", + }, }, - "id": "00000000-0000-0000-0000-000000000501", + "id": "00000000-0000-0000-0000-000000000502", "views": undefined, }, "timelineActivity": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000503", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000505", - }, - "happensAt": { - "id": "00000000-0000-0000-0000-000000000506", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000502", - }, - "linkedObjectMetadataId": { - "id": "00000000-0000-0000-0000-000000000521", - }, - "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000519", - }, - "linkedRecordId": { - "id": "00000000-0000-0000-0000-000000000520", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000507", - }, - "properties": { - "id": "00000000-0000-0000-0000-000000000508", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000511", - }, - "targetDashboard": { - "id": "00000000-0000-0000-0000-000000000518", - }, - "targetNote": { - "id": "00000000-0000-0000-0000-000000000514", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000512", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000510", - }, - "targetTask": { - "id": "00000000-0000-0000-0000-000000000513", - }, - "targetWorkflow": { - "id": "00000000-0000-0000-0000-000000000515", - }, - "targetWorkflowRun": { - "id": "00000000-0000-0000-0000-000000000517", - }, - "targetWorkflowVersion": { - "id": "00000000-0000-0000-0000-000000000516", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000504", }, - "workspaceMember": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000506", + }, + "happensAt": { + "id": "00000000-0000-0000-0000-000000000507", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000503", + }, + "linkedObjectMetadataId": { + "id": "00000000-0000-0000-0000-000000000522", + }, + "linkedRecordCachedName": { + "id": "00000000-0000-0000-0000-000000000520", + }, + "linkedRecordId": { + "id": "00000000-0000-0000-0000-000000000521", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000508", + }, + "properties": { "id": "00000000-0000-0000-0000-000000000509", }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000512", + }, + "targetDashboard": { + "id": "00000000-0000-0000-0000-000000000519", + }, + "targetNote": { + "id": "00000000-0000-0000-0000-000000000515", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000513", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000511", + }, + "targetTask": { + "id": "00000000-0000-0000-0000-000000000514", + }, + "targetWorkflow": { + "id": "00000000-0000-0000-0000-000000000516", + }, + "targetWorkflowRun": { + "id": "00000000-0000-0000-0000-000000000518", + }, + "targetWorkflowVersion": { + "id": "00000000-0000-0000-0000-000000000517", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000505", + }, + "workspaceMember": { + "id": "00000000-0000-0000-0000-000000000510", + }, }, - "id": "00000000-0000-0000-0000-000000000528", + "id": "00000000-0000-0000-0000-000000000529", "views": { "allTimelineActivities": { - "id": "00000000-0000-0000-0000-000000000527", + "id": "00000000-0000-0000-0000-000000000528", "viewFieldGroups": {}, "viewFields": { "happensAt": { - "id": "00000000-0000-0000-0000-000000000523", - }, - "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000526", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000522", - }, - "properties": { "id": "00000000-0000-0000-0000-000000000524", }, - "workspaceMember": { + "linkedRecordCachedName": { + "id": "00000000-0000-0000-0000-000000000527", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000523", + }, + "properties": { "id": "00000000-0000-0000-0000-000000000525", }, + "workspaceMember": { + "id": "00000000-0000-0000-0000-000000000526", + }, }, "viewGroups": {}, }, @@ -1751,80 +1754,80 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflow": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000542", - }, - "automatedTriggers": { - "id": "00000000-0000-0000-0000-000000000539", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000530", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000543", }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000532", - }, - "favorites": { + "automatedTriggers": { "id": "00000000-0000-0000-0000-000000000540", }, - "id": { - "id": "00000000-0000-0000-0000-000000000529", - }, - "lastPublishedVersionId": { - "id": "00000000-0000-0000-0000-000000000534", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000533", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000536", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000538", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000545", - }, - "statuses": { - "id": "00000000-0000-0000-0000-000000000535", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000541", - }, - "updatedAt": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000531", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000544", }, - "versions": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000533", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000541", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000530", + }, + "lastPublishedVersionId": { + "id": "00000000-0000-0000-0000-000000000535", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000534", + }, + "position": { "id": "00000000-0000-0000-0000-000000000537", }, + "runs": { + "id": "00000000-0000-0000-0000-000000000539", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000546", + }, + "statuses": { + "id": "00000000-0000-0000-0000-000000000536", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000542", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000532", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000545", + }, + "versions": { + "id": "00000000-0000-0000-0000-000000000538", + }, }, - "id": "00000000-0000-0000-0000-000000000553", + "id": "00000000-0000-0000-0000-000000000554", "views": { "allWorkflows": { - "id": "00000000-0000-0000-0000-000000000552", + "id": "00000000-0000-0000-0000-000000000553", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000549", + "id": "00000000-0000-0000-0000-000000000550", }, "name": { - "id": "00000000-0000-0000-0000-000000000546", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000551", - }, - "statuses": { "id": "00000000-0000-0000-0000-000000000547", }, - "updatedAt": { + "runs": { + "id": "00000000-0000-0000-0000-000000000552", + }, + "statuses": { "id": "00000000-0000-0000-0000-000000000548", }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000549", + }, "versions": { - "id": "00000000-0000-0000-0000-000000000550", + "id": "00000000-0000-0000-0000-000000000551", }, }, "viewGroups": {}, @@ -1834,116 +1837,116 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowAutomatedTrigger": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000555", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000557", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000554", - }, - "settings": { - "id": "00000000-0000-0000-0000-000000000559", - }, - "type": { - "id": "00000000-0000-0000-0000-000000000558", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000556", }, - "workflow": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000558", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000555", + }, + "settings": { "id": "00000000-0000-0000-0000-000000000560", }, + "type": { + "id": "00000000-0000-0000-0000-000000000559", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000557", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000561", + }, }, - "id": "00000000-0000-0000-0000-000000000561", + "id": "00000000-0000-0000-0000-000000000562", "views": undefined, }, "workflowRun": { "fields": { "context": { - "id": "00000000-0000-0000-0000-000000000577", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000563", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000574", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000565", - }, - "endedAt": { - "id": "00000000-0000-0000-0000-000000000571", - }, - "enqueuedAt": { - "id": "00000000-0000-0000-0000-000000000569", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000579", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000562", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000566", - }, - "output": { - "id": "00000000-0000-0000-0000-000000000576", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000573", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000581", - }, - "startedAt": { - "id": "00000000-0000-0000-0000-000000000570", - }, - "state": { "id": "00000000-0000-0000-0000-000000000578", }, - "status": { - "id": "00000000-0000-0000-0000-000000000572", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000580", - }, - "updatedAt": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000564", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000575", }, - "workflow": { - "id": "00000000-0000-0000-0000-000000000568", + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000566", }, - "workflowVersion": { + "endedAt": { + "id": "00000000-0000-0000-0000-000000000572", + }, + "enqueuedAt": { + "id": "00000000-0000-0000-0000-000000000570", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000580", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000563", + }, + "name": { "id": "00000000-0000-0000-0000-000000000567", }, + "output": { + "id": "00000000-0000-0000-0000-000000000577", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000574", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000582", + }, + "startedAt": { + "id": "00000000-0000-0000-0000-000000000571", + }, + "state": { + "id": "00000000-0000-0000-0000-000000000579", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000573", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000581", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000565", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000576", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000569", + }, + "workflowVersion": { + "id": "00000000-0000-0000-0000-000000000568", + }, }, - "id": "00000000-0000-0000-0000-000000000589", + "id": "00000000-0000-0000-0000-000000000590", "views": { "allWorkflowRuns": { - "id": "00000000-0000-0000-0000-000000000588", + "id": "00000000-0000-0000-0000-000000000589", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000586", + "id": "00000000-0000-0000-0000-000000000587", }, "name": { - "id": "00000000-0000-0000-0000-000000000582", - }, - "startedAt": { - "id": "00000000-0000-0000-0000-000000000585", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000584", - }, - "workflow": { "id": "00000000-0000-0000-0000-000000000583", }, + "startedAt": { + "id": "00000000-0000-0000-0000-000000000586", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000585", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000584", + }, "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000587", + "id": "00000000-0000-0000-0000-000000000588", }, }, "viewGroups": {}, @@ -1953,68 +1956,68 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowVersion": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000591", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000593", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000601", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000590", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000594", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000598", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000599", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000603", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000597", - }, - "steps": { - "id": "00000000-0000-0000-0000-000000000600", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000602", - }, - "trigger": { - "id": "00000000-0000-0000-0000-000000000596", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000592", }, - "workflow": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000594", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000602", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000591", + }, + "name": { "id": "00000000-0000-0000-0000-000000000595", }, + "position": { + "id": "00000000-0000-0000-0000-000000000599", + }, + "runs": { + "id": "00000000-0000-0000-0000-000000000600", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000604", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000598", + }, + "steps": { + "id": "00000000-0000-0000-0000-000000000601", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000603", + }, + "trigger": { + "id": "00000000-0000-0000-0000-000000000597", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000593", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000596", + }, }, - "id": "00000000-0000-0000-0000-000000000610", + "id": "00000000-0000-0000-0000-000000000611", "views": { "allWorkflowVersions": { - "id": "00000000-0000-0000-0000-000000000609", + "id": "00000000-0000-0000-0000-000000000610", "viewFieldGroups": {}, "viewFields": { "name": { - "id": "00000000-0000-0000-0000-000000000604", + "id": "00000000-0000-0000-0000-000000000605", }, "runs": { - "id": "00000000-0000-0000-0000-000000000608", + "id": "00000000-0000-0000-0000-000000000609", }, "status": { - "id": "00000000-0000-0000-0000-000000000606", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000607", }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000608", + }, "workflow": { - "id": "00000000-0000-0000-0000-000000000605", + "id": "00000000-0000-0000-0000-000000000606", }, }, "viewGroups": {}, @@ -2024,117 +2027,117 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workspaceMember": { "fields": { "accountOwnerForCompanies": { - "id": "00000000-0000-0000-0000-000000000625", - }, - "assignedTasks": { - "id": "00000000-0000-0000-0000-000000000622", - }, - "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000619", - }, - "blocklist": { - "id": "00000000-0000-0000-0000-000000000628", - }, - "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000629", - }, - "calendarStartDay": { - "id": "00000000-0000-0000-0000-000000000635", - }, - "colorScheme": { - "id": "00000000-0000-0000-0000-000000000617", - }, - "connectedAccounts": { "id": "00000000-0000-0000-0000-000000000626", }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000612", - }, - "dateFormat": { - "id": "00000000-0000-0000-0000-000000000632", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000614", - }, - "favorites": { - "id": "00000000-0000-0000-0000-000000000624", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000611", - }, - "locale": { - "id": "00000000-0000-0000-0000-000000000618", - }, - "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000627", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000616", - }, - "numberFormat": { - "id": "00000000-0000-0000-0000-000000000636", - }, - "ownedOpportunities": { + "assignedTasks": { "id": "00000000-0000-0000-0000-000000000623", }, - "position": { - "id": "00000000-0000-0000-0000-000000000615", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000634", - }, - "timeFormat": { - "id": "00000000-0000-0000-0000-000000000633", - }, - "timeZone": { - "id": "00000000-0000-0000-0000-000000000631", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000630", - }, - "updatedAt": { - "id": "00000000-0000-0000-0000-000000000613", - }, - "userEmail": { + "avatarUrl": { "id": "00000000-0000-0000-0000-000000000620", }, - "userId": { + "blocklist": { + "id": "00000000-0000-0000-0000-000000000629", + }, + "calendarEventParticipants": { + "id": "00000000-0000-0000-0000-000000000630", + }, + "calendarStartDay": { + "id": "00000000-0000-0000-0000-000000000636", + }, + "colorScheme": { + "id": "00000000-0000-0000-0000-000000000618", + }, + "connectedAccounts": { + "id": "00000000-0000-0000-0000-000000000627", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000613", + }, + "dateFormat": { + "id": "00000000-0000-0000-0000-000000000633", + }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000615", + }, + "favorites": { + "id": "00000000-0000-0000-0000-000000000625", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000612", + }, + "locale": { + "id": "00000000-0000-0000-0000-000000000619", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000628", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000617", + }, + "numberFormat": { + "id": "00000000-0000-0000-0000-000000000637", + }, + "ownedOpportunities": { + "id": "00000000-0000-0000-0000-000000000624", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000616", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000635", + }, + "timeFormat": { + "id": "00000000-0000-0000-0000-000000000634", + }, + "timeZone": { + "id": "00000000-0000-0000-0000-000000000632", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000631", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000614", + }, + "userEmail": { "id": "00000000-0000-0000-0000-000000000621", }, + "userId": { + "id": "00000000-0000-0000-0000-000000000622", + }, }, - "id": "00000000-0000-0000-0000-000000000647", + "id": "00000000-0000-0000-0000-000000000648", "views": { "allWorkspaceMembers": { - "id": "00000000-0000-0000-0000-000000000646", + "id": "00000000-0000-0000-0000-000000000647", "viewFieldGroups": {}, "viewFields": { "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000639", - }, - "colorScheme": { "id": "00000000-0000-0000-0000-000000000640", }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000645", - }, - "dateFormat": { - "id": "00000000-0000-0000-0000-000000000643", - }, - "locale": { + "colorScheme": { "id": "00000000-0000-0000-0000-000000000641", }, - "name": { - "id": "00000000-0000-0000-0000-000000000637", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000646", }, - "timeFormat": { + "dateFormat": { "id": "00000000-0000-0000-0000-000000000644", }, - "timeZone": { + "locale": { "id": "00000000-0000-0000-0000-000000000642", }, - "userEmail": { + "name": { "id": "00000000-0000-0000-0000-000000000638", }, + "timeFormat": { + "id": "00000000-0000-0000-0000-000000000645", + }, + "timeZone": { + "id": "00000000-0000-0000-0000-000000000643", + }, + "userEmail": { + "id": "00000000-0000-0000-0000-000000000639", + }, }, "viewGroups": {}, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-channel-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-channel-standard-flat-field-metadata.util.ts index 655b51f9bf..41a6db43de 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-channel-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-channel-standard-flat-field-metadata.util.ts @@ -517,6 +517,23 @@ export const buildMessageChannelStandardFlatFieldMetadatas = ({ twentyStandardApplicationId, now, }), + throttleRetryAfter: createStandardFieldFlatMetadata({ + objectName, + workspaceId, + context: { + fieldName: 'throttleRetryAfter', + type: FieldMetadataType.DATE_TIME, + label: 'Throttle Retry After', + description: 'Throttle Retry After', + icon: 'IconClock', + isNullable: true, + isUIReadOnly: true, + }, + standardObjectMetadataRelatedEntityIds, + dependencyFlatEntityMaps, + twentyStandardApplicationId, + now, + }), connectedAccount: createStandardRelationFieldFlatMetadata({ objectName, workspaceId, diff --git a/packages/twenty-server/src/modules/connected-account/utils/__tests__/is-throttled.spec.ts b/packages/twenty-server/src/modules/connected-account/utils/__tests__/is-throttled.spec.ts new file mode 100644 index 0000000000..5ceb925ab7 --- /dev/null +++ b/packages/twenty-server/src/modules/connected-account/utils/__tests__/is-throttled.spec.ts @@ -0,0 +1,52 @@ +import { isThrottled } from 'src/modules/connected-account/utils/is-throttled'; + +describe('isThrottled', () => { + it('should not throttle when no sync stage is active', () => { + expect(isThrottled(null, 3)).toBe(false); + }); + + it('should not throttle when there have been no failures', () => { + expect(isThrottled(new Date().toISOString(), 0)).toBe(false); + }); + + it('should keep throttling when retryAfter is in the future even though exponential backoff has expired', () => { + const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString(); + const fiveMinutesFromNow = new Date( + Date.now() + 5 * 60 * 1000, + ).toISOString(); + + expect(isThrottled(tenMinutesAgo, 1, fiveMinutesFromNow)).toBe(true); + }); + + it('should fall back to exponential backoff when retryAfter is not provided', () => { + const justNow = new Date().toISOString(); + + expect(isThrottled(justNow, 1)).toBe(true); + }); + + it('should fall back to exponential backoff when retryAfter is in the past', () => { + const justNow = new Date().toISOString(); + const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString(); + + expect(isThrottled(justNow, 1, fiveMinutesAgo)).toBe(true); + }); + + it('should fall back to exponential backoff when retryAfter is explicitly null', () => { + const justNow = new Date().toISOString(); + + expect(isThrottled(justNow, 1, null)).toBe(true); + }); + + it('should fall back to exponential backoff when retryAfter is an invalid date string', () => { + const justNow = new Date().toISOString(); + + expect(isThrottled(justNow, 1, 'not-a-date')).toBe(true); + }); + + it('should use exponential backoff when both backoff is active and retryAfter is in the past', () => { + const justNow = new Date().toISOString(); + const oneMinuteAgo = new Date(Date.now() - 60 * 1000).toISOString(); + + expect(isThrottled(justNow, 1, oneMinuteAgo)).toBe(true); + }); +}); diff --git a/packages/twenty-server/src/modules/connected-account/utils/is-throttled.ts b/packages/twenty-server/src/modules/connected-account/utils/is-throttled.ts index 0ab825bd77..1eaee3b899 100644 --- a/packages/twenty-server/src/modules/connected-account/utils/is-throttled.ts +++ b/packages/twenty-server/src/modules/connected-account/utils/is-throttled.ts @@ -1,8 +1,12 @@ +import { isDefined } from 'twenty-shared/utils'; + import { MESSAGING_THROTTLE_DURATION } from 'src/modules/messaging/message-import-manager/constants/messaging-throttle-duration'; +import { isValidDate } from 'src/utils/date/isValidDate'; export const isThrottled = ( syncStageStartedAt: string | null, throttleFailureCount: number, + throttleRetryAfter?: string | null, ): boolean => { if (!syncStageStartedAt) { return false; @@ -12,10 +16,25 @@ export const isThrottled = ( return false; } - return ( - computeThrottlePauseUntil(syncStageStartedAt, throttleFailureCount) > - new Date() + const now = new Date(); + + const exponentialBackoffUntil = computeThrottlePauseUntil( + syncStageStartedAt, + throttleFailureCount, ); + const retryAfterCandidate = isDefined(throttleRetryAfter) + ? new Date(throttleRetryAfter) + : null; + const retryAfterDate = isValidDate(retryAfterCandidate) + ? retryAfterCandidate + : null; + + const effectiveUntil = + isDefined(retryAfterDate) && retryAfterDate > exponentialBackoffUntil + ? retryAfterDate + : exponentialBackoffUntil; + + return effectiveUntil > now; }; const computeThrottlePauseUntil = ( diff --git a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts index d18e0486b0..91b6a4cdc8 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts @@ -116,6 +116,7 @@ export class MessageChannelSyncStatusService { syncCursor: '', syncStageStartedAt: null, throttleFailureCount: 0, + throttleRetryAfter: null, pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction.NONE, }); @@ -225,6 +226,7 @@ export class MessageChannelSyncStatusService { syncStatus: MessageChannelSyncStatus.ACTIVE, syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, throttleFailureCount: 0, + throttleRetryAfter: null, syncStageStartedAt: null, syncedAt: new Date().toISOString(), }); @@ -307,6 +309,7 @@ export class MessageChannelSyncStatusService { await messageChannelRepository.update(messageChannelIds, { syncStage: MessageChannelSyncStage.FAILED, syncStatus: syncStatus, + throttleRetryAfter: null, }); const metricsKey = diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts index 72c2fb75b2..01fc0e30a5 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts @@ -98,6 +98,7 @@ export class MessageChannelWorkspaceEntity extends BaseWorkspaceEntity { syncStage: MessageChannelSyncStage; syncStageStartedAt: string | null; throttleFailureCount: number; + throttleRetryAfter: string | null; connectedAccount: EntityRelation; connectedAccountId: string; messageChannelMessageAssociations: EntityRelation< diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts index f3f073e832..9f07a3a3fe 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts @@ -3,6 +3,7 @@ import { type MessageNetworkExceptionCode } from 'src/modules/messaging/message- export class MessageImportDriverException extends Error { code: MessageImportDriverExceptionCode | MessageNetworkExceptionCode; cause?: Error; + throttleRetryAfter?: Date; context?: { messageChannelId?: string; workspaceId?: string; @@ -14,6 +15,7 @@ export class MessageImportDriverException extends Error { code: MessageImportDriverExceptionCode | MessageNetworkExceptionCode, options?: { cause?: Error; + throttleRetryAfter?: Date; context?: { messageChannelId?: string; workspaceId?: string; @@ -25,6 +27,7 @@ export class MessageImportDriverException extends Error { this.name = 'MessageImportDriverException'; this.code = code; this.cause = options?.cause; + this.throttleRetryAfter = options?.throttleRetryAfter; this.context = options?.context; if (options?.cause?.stack) { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks.ts index e67ea8c3bd..c5ba34d656 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks.ts @@ -50,9 +50,11 @@ const ERROR_DEFINITIONS: Record> = { export const getGmailApiError = ({ code, reason, + message, }: { code: number; reason?: string; + message?: string; }): GaxiosError => { const statusMap = ERROR_DEFINITIONS[code]; @@ -62,8 +64,10 @@ export const getGmailApiError = ({ const config = statusMap[reason || ''] ?? statusMap.default; + const errorMessage = message ?? config.message; + return new GaxiosError( - config.message, + errorMessage, { url: 'https://gmail.googleapis.com/mocks' }, { status: code, @@ -71,10 +75,10 @@ export const getGmailApiError = ({ data: { error: { code, - message: config.message, + message: errorMessage, errors: [ { - message: config.message, + message: errorMessage, reason: config.reason, }, ], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-error-retry-after.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-error-retry-after.spec.ts new file mode 100644 index 0000000000..db5fb05415 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-error-retry-after.spec.ts @@ -0,0 +1,40 @@ +import { parseGmailErrorRetryAfter } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util'; + +describe('parseGmailErrorRetryAfter', () => { + it('should extract the retry-after date from a Gmail 429 error message', () => { + const fifteenMinutesFromNow = new Date(Date.now() + 15 * 60 * 1000); + const message = `User-rate limit exceeded. Retry after ${fifteenMinutesFromNow.toISOString()}`; + + const result = parseGmailErrorRetryAfter(message); + + expect(result).toBeInstanceOf(Date); + expect(result!.getTime()).toBeCloseTo(fifteenMinutesFromNow.getTime(), -3); + }); + + it('should return undefined when the message contains no retry-after timestamp', () => { + expect( + parseGmailErrorRetryAfter('Too Many Concurrent Requests'), + ).toBeUndefined(); + }); + + it('should return undefined when the retry-after timestamp has already passed', () => { + const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000); + const message = `User-rate limit exceeded. Retry after ${fiveMinutesAgo.toISOString()}`; + + expect(parseGmailErrorRetryAfter(message)).toBeUndefined(); + }); + + it('should return undefined for an empty string', () => { + expect(parseGmailErrorRetryAfter('')).toBeUndefined(); + }); + + it('should match case variations of "Retry after"', () => { + const fifteenMinutesFromNow = new Date(Date.now() + 15 * 60 * 1000); + const message = `User-rate limit exceeded. retry after ${fifteenMinutesFromNow.toISOString()}`; + + const result = parseGmailErrorRetryAfter(message); + + expect(result).toBeInstanceOf(Date); + expect(result!.getTime()).toBeCloseTo(fifteenMinutesFromNow.getTime(), -3); + }); +}); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-message-list-fetch-error.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-message-list-fetch-error.spec.ts index 1d5df5f157..4ed6004413 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-message-list-fetch-error.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-gmail-message-list-fetch-error.spec.ts @@ -232,4 +232,48 @@ describe('parseGmailApiError', () => { MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR, ); }); + + it('should populate retryAfter on the exception when a 429 contains a retry-after timestamp', () => { + const fifteenMinutesFromNow = new Date(Date.now() + 15 * 60 * 1000); + const error = getGmailApiError({ + code: 429, + message: `User-rate limit exceeded. Retry after ${fifteenMinutesFromNow.toISOString()}`, + }); + + const exception = parseGmailApiError(error); + + expect(exception.throttleRetryAfter).toBeInstanceOf(Date); + expect(exception.throttleRetryAfter!.getTime()).toBeCloseTo( + fifteenMinutesFromNow.getTime(), + -3, + ); + }); + + it('should leave retryAfter undefined on 429 without a retry-after timestamp', () => { + const error = getGmailApiError({ code: 429 }); + + const exception = parseGmailApiError(error); + + expect(exception.throttleRetryAfter).toBeUndefined(); + }); + + it('should populate retryAfter on the exception when a 403 rateLimitExceeded contains a retry-after timestamp', () => { + const fifteenMinutesFromNow = new Date(Date.now() + 15 * 60 * 1000); + const error = getGmailApiError({ + code: 403, + reason: 'rateLimit', + message: `Rate Limit Exceeded. Retry after ${fifteenMinutesFromNow.toISOString()}`, + }); + + const exception = parseGmailApiError(error); + + expect(exception.code).toBe( + MessageImportDriverExceptionCode.TEMPORARY_ERROR, + ); + expect(exception.throttleRetryAfter).toBeInstanceOf(Date); + expect(exception.throttleRetryAfter!.getTime()).toBeCloseTo( + fifteenMinutesFromNow.getTime(), + -3, + ); + }); }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util.ts index 3d153cd9a2..c2192dbefe 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util.ts @@ -4,6 +4,7 @@ import { MessageImportDriverException, MessageImportDriverExceptionCode, } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; +import { parseGmailErrorRetryAfter } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util'; export const parseGmailApiError = ( error: GaxiosError, @@ -57,6 +58,9 @@ export const parseGmailApiError = ( return new MessageImportDriverException( gmailApiError.message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { + throttleRetryAfter: parseGmailErrorRetryAfter(gmailApiError.message), + }, ); case 403: @@ -68,6 +72,11 @@ export const parseGmailApiError = ( return new MessageImportDriverException( gmailApiError.message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { + throttleRetryAfter: parseGmailErrorRetryAfter( + gmailApiError.message, + ), + }, ); } if (gmailApiError.reason === 'domainPolicy') { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util.ts new file mode 100644 index 0000000000..e612503001 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-error-retry-after.util.ts @@ -0,0 +1,24 @@ +const RETRY_AFTER_REGEX = + /Retry after (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)/i; + +export const parseGmailErrorRetryAfter = ( + message: string, +): Date | undefined => { + const match = message.match(RETRY_AFTER_REGEX); + + if (!match) { + return undefined; + } + + const retryAfter = new Date(match[1]); + + if (isNaN(retryAfter.getTime())) { + return undefined; + } + + if (retryAfter <= new Date()) { + return undefined; + } + + return retryAfter; +}; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts index da5a4e7c47..0c797a28b2 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts @@ -84,6 +84,7 @@ export class MessagingMessageListFetchJob { isThrottled( messageChannel.syncStageStartedAt, messageChannel.throttleFailureCount, + messageChannel.throttleRetryAfter, ) ) { await this.messageChannelSyncStatusService.markAsMessagesListFetchPending( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts index 17d54afee2..a0ac85a9c9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts @@ -82,6 +82,7 @@ export class MessagingMessagesImportJob { isThrottled( messageChannel.syncStageStartedAt, messageChannel.throttleFailureCount, + messageChannel.throttleRetryAfter, ) ) { await this.messageChannelSyncStatusService.markAsMessagesImportPending( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts index 71c23ac846..fc43c3e143 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts @@ -38,6 +38,7 @@ export class MessagingCursorService { }, { throttleFailureCount: 0, + throttleRetryAfter: null, syncStageStartedAt: null, syncCursor: !messageChannel.syncCursor || @@ -61,6 +62,7 @@ export class MessagingCursorService { }, { throttleFailureCount: 0, + throttleRetryAfter: null, syncStageStartedAt: null, }, ); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts index ebf85b7501..dc3fa1e16f 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common'; +import { isDefined } from 'twenty-shared/utils'; + import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service'; import { type TwentyORMException, @@ -166,6 +168,20 @@ export class MessageImportExceptionHandlerService { undefined, ['throttleFailureCount', 'id'], ); + + const throttleRetryAfter = + exception instanceof MessageImportDriverException + ? exception.throttleRetryAfter + : undefined; + + await messageChannelRepository.update( + { id: messageChannel.id }, + { + throttleRetryAfter: isDefined(throttleRetryAfter) + ? throttleRetryAfter.toISOString() + : null, + }, + ); }, authContext); switch (syncStep) { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts index dbe2e2b20f..a9e36ce186 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts @@ -189,6 +189,7 @@ export class MessagingMessagesImportService { }, { throttleFailureCount: 0, + throttleRetryAfter: null, syncStageStartedAt: null, }, ); diff --git a/packages/twenty-shared/src/metadata/standard-object.constant.ts b/packages/twenty-shared/src/metadata/standard-object.constant.ts index 5afc9a695c..864571cd1e 100644 --- a/packages/twenty-shared/src/metadata/standard-object.constant.ts +++ b/packages/twenty-shared/src/metadata/standard-object.constant.ts @@ -821,6 +821,9 @@ export const STANDARD_OBJECTS = { throttleFailureCount: { universalIdentifier: '20202020-0291-42be-9ad0-d578a51684ab', }, + throttleRetryAfter: { + universalIdentifier: '20202020-a1e3-4d7f-b5c2-9f8e6d4c3b2a', + }, }, indexes: { connectedAccountIdIndex: {