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:
+3
@@ -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();
|
||||
|
||||
+306
@@ -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();
|
||||
};
|
||||
+9
@@ -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,
|
||||
|
||||
+26
@@ -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',
|
||||
},
|
||||
];
|
||||
|
||||
+14
@@ -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(),
|
||||
|
||||
+94
@@ -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,
|
||||
},
|
||||
];
|
||||
+10
-1
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user