feat: secure and user-scope metadata resolvers for messaging infrastructure (#18787)

## Summary

Builds on the messaging infrastructure migration (#18784) by securing
and user-scoping all 4 metadata resolvers:

### DTOs secured
- **ConnectedAccountDTO**: `@HideField()` on `accessToken`,
`refreshToken`, `connectionParameters`, `oidcTokenClaims`
- **MessageChannelDTO / CalendarChannelDTO**: `@HideField()` on
`syncCursor`
- **MessageFolderDTO**: `@HideField()` on `syncCursor`, `externalId`
- **UpdateMessageFolderInputUpdates**: stripped to only `isSynced`
(removed `name`, `syncCursor`, `pendingSyncAction`)

### Resolvers user-scoped via `@AuthUserWorkspaceId()`
- `myConnectedAccounts` — returns only the calling user's accounts (no
permission guard)
- `myMessageChannels(connectedAccountId?)` — returns channels for the
user's connected accounts
- `myCalendarChannels(connectedAccountId?)` — same pattern
- `myMessageFolders(messageChannelId?)` — returns folders through the
ownership chain

### Admin-only listing with permission guard
- `connectedAccounts` query retained with
`SettingsPermissionGuard(CONNECTED_ACCOUNTS)` for admin listing of all
workspace accounts

### Unsafe mutations removed
- Removed `createConnectedAccount`, `updateConnectedAccount` (OAuth/IMAP
flows create/refresh tokens server-side)
- Removed `create*`/`delete*` mutations from MessageChannel,
CalendarChannel, MessageFolder (managed by sync engine)

### Update mutations restricted with ownership verification
- `deleteConnectedAccount(id)` — verifies `entity.userWorkspaceId ===
currentUserWorkspaceId`
- `updateMessageChannel` / `updateCalendarChannel` /
`updateMessageFolder` — verify ownership through connected account chain
- New `OWNERSHIP_VIOLATION` exception codes map to `ForbiddenError` in
GraphQL

### `@AuthUserWorkspaceId` decorator hardened
- Added `allowUndefined` option (default: `false`) — throws
`ForbiddenException` if `userWorkspaceId` is undefined (e.g. API key
auth)
- Existing callers updated to `@AuthUserWorkspaceId({ allowUndefined:
true })` where needed
- New user-scoped resolvers enforce non-undefined `userWorkspaceId` at
decorator level

### Exception handler chaining
- `MessageFolderGraphqlApiExceptionInterceptor`,
`MessageChannelGraphqlApiExceptionInterceptor`,
`CalendarChannelGraphqlApiExceptionInterceptor` chain upstream exception
handling (ConnectedAccountException, MessageChannelException) for
correct `ForbiddenError` propagation

### Metadata services enhanced
- `findByUserWorkspaceId()`, `getUserConnectedAccountIds()`,
`findByConnectedAccountIds()`, `findByMessageChannelIds()`
- `findBy*ForUser()` methods encapsulate ownership checks before
querying
- `verifyOwnership()` on all 4 services with proper chain validation
- Named parameters throughout for clarity

### Dev seeds for both schemas
- Added JANE to connected account, message channel, calendar channel
workspace seeds
- Created message folder workspace seeds (TIM, JONY, JANE)
- New `seed-metadata-entities.util.ts` seeds core schema tables
(connectedAccount, messageChannel, calendarChannel, messageFolder) with
same IDs as workspace seeds, mapping `accountOwnerId` →
`userWorkspaceId`

### Integration tests (using seeds, not raw SQL)
- 4 test suites (`connected-account`, `message-channel`,
`calendar-channel`, `message-folder`)
- Tests use seeded data IDs from seed constants — no raw SQL
inserts/deletes
- Tests read via GraphQL resolvers
- Tests cover: user scoping, admin permission checks, sensitive field
exclusion, ownership enforcement on mutations

### Frontend migration
- Feature-flag-gated hooks (`useMyConnectedAccounts`,
`useMyMessageChannels`, `useMyCalendarChannels`, `useMyMessageFolders`)
- When `IS_CONNECTED_ACCOUNT_MIGRATED` is on: hooks use metadata API
(`POST /metadata`)
- When flag is off: hooks use existing workspace API (`POST /graphql`,
current behavior)
- Settings account pages updated to use new hooks
- `useEffect` extracted to
`SettingsAccountsSelectedMessageChannelEffect` component per project
conventions
- Error messages translated with Lingui

## Test plan
- [x] Server typecheck passes
- [x] Server lint passes
- [x] Server unit tests pass (477 suites, 4269 tests)
- [x] Frontend typecheck passes
- [x] Frontend lint passes
- [x] Integration tests verify user-scoping, ownership enforcement,
hidden fields
- [ ] CI green

---------

Co-authored-by: neo773 <neo773@protonmail.com>
This commit is contained in:
Charles Bochet
2026-03-20 17:22:22 +01:00
committed by GitHub
parent a8625d8bfb
commit 9cb21e71fa
88 changed files with 3412 additions and 1233 deletions
@@ -12,6 +12,7 @@ import { seedAgents } from 'src/engine/workspace-manager/dev-seeder/core/utils/s
import { seedApiKeys } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-api-keys.util';
import { seedFeatureFlags } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util';
import { seedMetadataEntities } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-metadata-entities.util';
import { seedServerId } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-server-id.util';
import { seedUserWorkspaces } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import { seedUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
@@ -93,6 +94,8 @@ export const seedCoreSchema = async ({
await seedBillingSubscriptions({ queryRunner, schemaName, workspaceId });
}
await seedMetadataEntities({ queryRunner, schemaName, workspaceId });
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
@@ -0,0 +1,306 @@
import { type QueryRunner } from 'typeorm';
import {
CalendarChannelVisibility,
MessageChannelSyncStage,
MessageChannelType,
MessageChannelVisibility,
MessageFolderPendingSyncAction,
} from 'twenty-shared/types';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import { CALENDAR_CHANNEL_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-channel-data-seeds.constant';
import { CONNECTED_ACCOUNT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/connected-account-data-seeds.constant';
import { MESSAGE_CHANNEL_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant';
import { MESSAGE_FOLDER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-folder-data-seeds.constant';
type SeedMetadataEntitiesArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedMetadataEntities = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedMetadataEntitiesArgs) => {
if (
workspaceId !== SEED_APPLE_WORKSPACE_ID &&
workspaceId !== SEED_YCOMBINATOR_WORKSPACE_ID
) {
return;
}
if (workspaceId === SEED_YCOMBINATOR_WORKSPACE_ID) {
return;
}
await seedConnectedAccounts({ queryRunner, schemaName, workspaceId });
await seedMessageChannels({ queryRunner, schemaName, workspaceId });
await seedCalendarChannels({ queryRunner, schemaName, workspaceId });
await seedMessageFolders({ queryRunner, schemaName, workspaceId });
};
const seedConnectedAccounts = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedMetadataEntitiesArgs) => {
const connectedAccounts = [
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
provider: 'google',
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.TIM,
workspaceId,
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
handle: 'jony.ive@apple.dev',
provider: 'google',
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.JONY,
workspaceId,
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.PHIL,
handle: 'phil.schiler@apple.dev',
provider: 'google',
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.PHIL,
workspaceId,
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
handle: 'jane.austen@apple.dev',
provider: 'google',
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.JANE,
workspaceId,
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE_DELETABLE,
handle: 'jane-deletable@apple.dev',
provider: 'google',
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.JANE,
workspaceId,
},
];
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.connectedAccount`, [
'id',
'handle',
'provider',
'userWorkspaceId',
'workspaceId',
])
.orIgnore()
.values(connectedAccounts)
.execute();
};
const seedMessageChannels = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedMetadataEntitiesArgs) => {
const messageChannels = [
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
type: MessageChannelType.EMAIL,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'SENT_AND_RECEIVED',
messageFolderImportPolicy: 'ALL_FOLDERS',
excludeNonProfessionalEmails: false,
excludeGroupEmails: false,
pendingGroupEmailsAction: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
workspaceId,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.JONY,
handle: 'jony.ive@apple.dev',
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
type: MessageChannelType.EMAIL,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'SENT_AND_RECEIVED',
messageFolderImportPolicy: 'ALL_FOLDERS',
excludeNonProfessionalEmails: false,
excludeGroupEmails: false,
pendingGroupEmailsAction: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
workspaceId,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
handle: 'jane.austen@apple.dev',
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
type: MessageChannelType.EMAIL,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'SENT_AND_RECEIVED',
messageFolderImportPolicy: 'ALL_FOLDERS',
excludeNonProfessionalEmails: false,
excludeGroupEmails: false,
pendingGroupEmailsAction: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
workspaceId,
},
];
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.messageChannel`, [
'id',
'handle',
'visibility',
'type',
'syncStage',
'isContactAutoCreationEnabled',
'contactAutoCreationPolicy',
'messageFolderImportPolicy',
'excludeNonProfessionalEmails',
'excludeGroupEmails',
'pendingGroupEmailsAction',
'isSyncEnabled',
'connectedAccountId',
'workspaceId',
])
.orIgnore()
.values(messageChannels)
.execute();
};
const seedCalendarChannels = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedMetadataEntitiesArgs) => {
const calendarChannels = [
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
visibility: CalendarChannelVisibility.METADATA,
syncStage: 'CALENDAR_EVENT_LIST_FETCH_PENDING',
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
workspaceId,
},
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.JONY,
handle: 'jony@apple.dev',
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
syncStage: 'CALENDAR_EVENT_LIST_FETCH_PENDING',
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
workspaceId,
},
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.JANE,
handle: 'jane.austen@apple.dev',
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
syncStage: 'CALENDAR_EVENT_LIST_FETCH_PENDING',
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy: 'NONE',
isSyncEnabled: true,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
workspaceId,
},
];
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.calendarChannel`, [
'id',
'handle',
'visibility',
'syncStage',
'isContactAutoCreationEnabled',
'contactAutoCreationPolicy',
'isSyncEnabled',
'connectedAccountId',
'workspaceId',
])
.orIgnore()
.values(calendarChannels)
.execute();
};
const seedMessageFolders = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedMetadataEntitiesArgs) => {
const messageFolders = [
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.TIM_INBOX,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
workspaceId,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JONY_INBOX,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JONY,
workspaceId,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JANE_INBOX,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
workspaceId,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JANE_SENT,
name: 'Sent',
isSynced: true,
isSentFolder: true,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
workspaceId,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
];
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.messageFolder`, [
'id',
'name',
'isSynced',
'isSentFolder',
'messageChannelId',
'workspaceId',
'pendingSyncAction',
])
.orIgnore()
.values(messageFolders)
.execute();
};
@@ -26,6 +26,7 @@ const GENERATE_CALENDAR_CHANNEL_IDS = (): Record<string, string> => {
CHANNEL_IDS['TIM'] = '20202020-a40f-4faf-bb9f-c6f9945b8203';
CHANNEL_IDS['JONY'] = '20202020-a40f-4faf-bb9f-c6f9945b8204';
CHANNEL_IDS['PHIL'] = '20202020-a40f-4faf-bb9f-c6f9945b8205';
CHANNEL_IDS['JANE'] = '20202020-a40f-4faf-bb9f-c6f9945b8208';
CHANNEL_IDS['COMPANY_MAIN'] = '20202020-a40f-4faf-bb9f-c6f9945b8206';
CHANNEL_IDS['TEAM_CALENDAR'] = '20202020-a40f-4faf-bb9f-c6f9945b8207';
@@ -59,6 +60,14 @@ export const CALENDAR_CHANNEL_DATA_SEEDS: CalendarChannelDataSeed[] = [
isContactAutoCreationEnabled: true,
isSyncEnabled: true,
},
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.JANE,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
handle: 'jane.austen@apple.dev',
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
isContactAutoCreationEnabled: true,
isSyncEnabled: true,
},
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.COMPANY_MAIN,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
@@ -31,6 +31,8 @@ export const CONNECTED_ACCOUNT_DATA_SEED_IDS = {
TIM: '20202020-9ac0-4390-9a1a-ab4d2c4e1bb7',
JONY: '20202020-0cc8-4d60-a3a4-803245698908',
PHIL: '20202020-cafc-4323-908d-e5b42ad69fdf',
JANE: '20202020-b5c7-46f0-bf5c-3f4e4b3f7c1a',
JANE_DELETABLE: '20202020-d1e5-4a8f-9c3b-7f6d5e4c3b2a',
};
export const CONNECTED_ACCOUNT_DATA_SEEDS: ConnectedAccountDataSeed[] = [
@@ -70,4 +72,28 @@ export const CONNECTED_ACCOUNT_DATA_SEEDS: ConnectedAccountDataSeed[] = [
provider: 'google',
handle: 'phil.schiler@apple.dev',
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'jane.austen@apple.dev',
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE_DELETABLE,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'jane-deletable@apple.dev',
},
];
@@ -40,6 +40,7 @@ const GENERATE_MESSAGE_CHANNEL_IDS = (): Record<string, string> => {
CHANNEL_IDS['TIM'] = '20202020-9b80-4c2c-a597-383db48de1d6';
CHANNEL_IDS['JONY'] = '20202020-5ffe-4b32-814a-983d5e4911cd';
CHANNEL_IDS['PHIL'] = '20202020-e2f1-49b5-85d2-5d3a3386990c';
CHANNEL_IDS['JANE'] = '20202020-8c4d-4e71-a672-2e6a8c9f1b3d';
CHANNEL_IDS['SUPPORT'] = '20202020-e2f1-49b5-85d2-5d3a3386990d';
CHANNEL_IDS['SALES'] = '20202020-e2f1-49b5-85d2-5d3a3386990e';
@@ -88,6 +89,19 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JANE,
handle: 'jane.austen@apple.dev',
isSyncEnabled: true,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.SUPPORT,
createdAt: new Date(),
@@ -0,0 +1,94 @@
import { MessageFolderPendingSyncAction } from 'twenty-shared/types';
import { MESSAGE_CHANNEL_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant';
type MessageFolderDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
name: string;
isSynced: boolean;
isSentFolder: boolean;
messageChannelId: string;
pendingSyncAction: MessageFolderPendingSyncAction;
};
export const MESSAGE_FOLDER_DATA_SEED_COLUMNS: (keyof MessageFolderDataSeed)[] =
[
'id',
'createdAt',
'updatedAt',
'deletedAt',
'name',
'isSynced',
'isSentFolder',
'messageChannelId',
'pendingSyncAction',
];
export const MESSAGE_FOLDER_DATA_SEED_IDS = {
TIM_INBOX: '20202020-f1a2-4b3c-8d4e-5f6a7b8c9d0e',
TIM_SENT: '20202020-f1a2-4b3c-8d4e-5f6a7b8c9d1e',
JONY_INBOX: '20202020-f1a2-4b3c-8d4e-5f6a7b8c9d2e',
JANE_INBOX: '20202020-f1a2-4b3c-8d4e-5f6a7b8c9d3e',
JANE_SENT: '20202020-f1a2-4b3c-8d4e-5f6a7b8c9d4e',
};
export const MESSAGE_FOLDER_DATA_SEEDS: MessageFolderDataSeed[] = [
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.TIM_INBOX,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.TIM_SENT,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
name: 'Sent',
isSynced: true,
isSentFolder: true,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JONY_INBOX,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JONY,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JANE_INBOX,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
name: 'INBOX',
isSynced: true,
isSentFolder: false,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
{
id: MESSAGE_FOLDER_DATA_SEED_IDS.JANE_SENT,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
name: 'Sent',
isSynced: true,
isSentFolder: true,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.JANE,
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
];
@@ -56,6 +56,10 @@ import {
MESSAGE_CHANNEL_DATA_SEED_COLUMNS,
MESSAGE_CHANNEL_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant';
import {
MESSAGE_FOLDER_DATA_SEED_COLUMNS,
MESSAGE_FOLDER_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-folder-data-seeds.constant';
import {
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_COLUMNS,
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEEDS,
@@ -194,8 +198,13 @@ const getRecordSeedsBatches = (
},
];
// Batch 4: Depends on person/company or independent
// Batch 4: Depends on person/company/messageChannel or independent
const batch4: RecordSeedConfig[] = [
{
tableName: 'messageFolder',
pgColumns: MESSAGE_FOLDER_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_FOLDER_DATA_SEEDS,
},
{
tableName: 'opportunity',
pgColumns: OPPORTUNITY_DATA_SEED_COLUMNS,