mirror of
https://github.com/twentyhq/twenty.git
synced 2026-06-12 09:57:03 -04:00
feat: configure standard views and migrate attachment seeds to FILES field (#17958)
## Summary
- Add default visible view fields for `timelineActivity`, `attachment`,
`noteTarget`, `taskTarget`, and `workspaceMember` objects so they
display useful columns out of the box
- Standardize morph relation field labels to "Target" with
`IconArrowUpRight` for consistency across all pivot/junction tables
- Mark deprecated fields (`fullPath`, `fileCategory`,
`linkedRecordCachedName`, `linkedRecordId`, `linkedObjectMetadataId`) as
`isSystem` to hide them from the UI column picker
- Fix morph field deduplication logic (`pickMorphGroupSurvivor`) to
prefer active, non-system fields over auto-generated system fields from
custom objects
- Migrate attachment seeds from legacy `fullPath`/`fileCategory` to the
new `FILES` field type, creating proper `FileEntity` records in
`core.file` via `fileStorageService.writeFile()`
- Restore `customDomain` in the user query fragment
<img width="825" height="754" alt="Screenshot 2026-02-15 at 15 44 27"
src="https://github.com/user-attachments/assets/9596a3dd-8d3a-43c0-925a-0adef9ee68a8"
/>
<img width="736" height="731" alt="Screenshot 2026-02-15 at 15 44 13"
src="https://github.com/user-attachments/assets/cd1a66c5-731d-43e6-bbc3-703cbeda1652"
/>
<img width="722" height="757" alt="Screenshot 2026-02-15 at 15 44 03"
src="https://github.com/user-attachments/assets/b5210546-6a40-4940-8e4f-874818a614fb"
/>
<img width="907" height="757" alt="Screenshot 2026-02-15 at 15 43 52"
src="https://github.com/user-attachments/assets/ead5b9a8-1989-4d68-9640-583da6233711"
/>
<img width="1002" height="731" alt="Screenshot 2026-02-15 at 15 43 38"
src="https://github.com/user-attachments/assets/38accb8c-f5d5-4bfc-b245-06389849810b"
/>
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches migration/upgrade commands that write to core metadata tables
and adjust field/view definitions, plus changes dev seeding to create
`core.file` records; mistakes could affect UI visibility or seed
integrity across workspaces.
>
> **Overview**
> Adds a new `upgrade:1-18:backfill-standard-views-and-field-metadata`
command that, per workspace, marks specific fields as `isSystem`,
normalizes morph-relation field `label`/`icon` to
`Target`/`IconArrowUpRight`, and backfills missing standard
`view`/`viewField` rows for `attachment`, `noteTarget`, `taskTarget`,
`timelineActivity`, and `workspaceMember`, followed by cache
invalidation + metadata version bump.
>
> Refactors morph-relation deduplication to pick a single survivor per
`morphId` using a new `pickMorphGroupSurvivor` rule (prefer active +
non-system, then smallest id), with new unit tests.
>
> Updates standard metadata generators and snapshots to reflect the new
system flags and default view fields, and rewrites attachment dev
seeding to populate the new `file` (FILES field) JSON and create
corresponding `core.file` entries via `FileStorageService.writeFile`
with workspace-scoped file IDs.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b1939bbf6f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,753 @@
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { DataSource, 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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/workspace-cache-key.type';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
|
||||
// Fields that should be marked as isSystem: true
|
||||
const FIELDS_TO_MARK_AS_SYSTEM = [
|
||||
// attachment: deprecated fields
|
||||
STANDARD_OBJECTS.attachment.fields.fullPath.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.fileCategory.universalIdentifier,
|
||||
// timelineActivity: linked record fields
|
||||
STANDARD_OBJECTS.timelineActivity.fields.linkedRecordCachedName
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.linkedRecordId.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.linkedObjectMetadataId
|
||||
.universalIdentifier,
|
||||
// workspaceMember: relational fields
|
||||
STANDARD_OBJECTS.workspaceMember.fields.favorites.universalIdentifier,
|
||||
STANDARD_OBJECTS.workspaceMember.fields.accountOwnerForCompanies
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.workspaceMember.fields.connectedAccounts.universalIdentifier,
|
||||
STANDARD_OBJECTS.workspaceMember.fields.messageParticipants
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.workspaceMember.fields.blocklist.universalIdentifier,
|
||||
STANDARD_OBJECTS.workspaceMember.fields.calendarEventParticipants
|
||||
.universalIdentifier,
|
||||
];
|
||||
|
||||
// Morph target fields that should have label "Target" and icon "IconArrowUpRight"
|
||||
const MORPH_FIELDS_TO_RELABEL = [
|
||||
// attachment
|
||||
STANDARD_OBJECTS.attachment.fields.targetTask.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetNote.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetPerson.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetCompany.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetOpportunity.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetDashboard.universalIdentifier,
|
||||
STANDARD_OBJECTS.attachment.fields.targetWorkflow.universalIdentifier,
|
||||
// noteTarget
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetPerson.universalIdentifier,
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetCompany.universalIdentifier,
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetOpportunity.universalIdentifier,
|
||||
// taskTarget
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetPerson.universalIdentifier,
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetCompany.universalIdentifier,
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetOpportunity.universalIdentifier,
|
||||
// timelineActivity
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetPerson.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetCompany.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetOpportunity
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetTask.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetNote.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflow.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowVersion
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowRun
|
||||
.universalIdentifier,
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetDashboard.universalIdentifier,
|
||||
];
|
||||
|
||||
interface ViewFieldDef {
|
||||
fieldUI: string;
|
||||
viewFieldUI: string;
|
||||
position: number;
|
||||
}
|
||||
|
||||
interface ViewToBackfill {
|
||||
objectUI: string;
|
||||
viewName: string;
|
||||
viewIcon: string;
|
||||
viewUI: string;
|
||||
viewFields: ViewFieldDef[];
|
||||
}
|
||||
|
||||
const VIEWS_TO_BACKFILL: ViewToBackfill[] = [
|
||||
{
|
||||
objectUI: STANDARD_OBJECTS.attachment.universalIdentifier,
|
||||
viewName: 'All Attachments',
|
||||
viewIcon: 'IconList',
|
||||
viewUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.universalIdentifier,
|
||||
viewFields: [
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.attachment.fields.name.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.name
|
||||
.universalIdentifier,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.attachment.fields.file.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.file
|
||||
.universalIdentifier,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetPerson.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields
|
||||
.targetPerson.universalIdentifier,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetCompany.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields
|
||||
.targetCompany.universalIdentifier,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetOpportunity
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields
|
||||
.targetOpportunity.universalIdentifier,
|
||||
position: 4,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetTask.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.targetTask
|
||||
.universalIdentifier,
|
||||
position: 5,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetNote.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.targetNote
|
||||
.universalIdentifier,
|
||||
position: 6,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetDashboard
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields
|
||||
.targetDashboard.universalIdentifier,
|
||||
position: 7,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.targetWorkflow.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields
|
||||
.targetWorkflow.universalIdentifier,
|
||||
position: 8,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.createdBy.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.createdBy
|
||||
.universalIdentifier,
|
||||
position: 9,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.attachment.fields.createdAt.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.attachment.views.allAttachments.viewFields.createdAt
|
||||
.universalIdentifier,
|
||||
position: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
objectUI: STANDARD_OBJECTS.noteTarget.universalIdentifier,
|
||||
viewName: 'All Note Targets',
|
||||
viewIcon: 'IconList',
|
||||
viewUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.universalIdentifier,
|
||||
viewFields: [
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.noteTarget.fields.id.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.viewFields.id
|
||||
.universalIdentifier,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.noteTarget.fields.note.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.viewFields.note
|
||||
.universalIdentifier,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetPerson.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.viewFields
|
||||
.targetPerson.universalIdentifier,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetCompany.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.viewFields
|
||||
.targetCompany.universalIdentifier,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.fields.targetOpportunity
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.noteTarget.views.allNoteTargets.viewFields
|
||||
.targetOpportunity.universalIdentifier,
|
||||
position: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
objectUI: STANDARD_OBJECTS.taskTarget.universalIdentifier,
|
||||
viewName: 'All Task Targets',
|
||||
viewIcon: 'IconList',
|
||||
viewUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.universalIdentifier,
|
||||
viewFields: [
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.taskTarget.fields.id.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.viewFields.id
|
||||
.universalIdentifier,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
fieldUI: STANDARD_OBJECTS.taskTarget.fields.task.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.viewFields.task
|
||||
.universalIdentifier,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetPerson.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.viewFields
|
||||
.targetPerson.universalIdentifier,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetCompany.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.viewFields
|
||||
.targetCompany.universalIdentifier,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.fields.targetOpportunity
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.taskTarget.views.allTaskTargets.viewFields
|
||||
.targetOpportunity.universalIdentifier,
|
||||
position: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
objectUI: STANDARD_OBJECTS.timelineActivity.universalIdentifier,
|
||||
viewName: 'All Timeline Activities',
|
||||
viewIcon: 'IconList',
|
||||
viewUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.universalIdentifier,
|
||||
viewFields: [
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.name.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.name.universalIdentifier,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.happensAt
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.happensAt.universalIdentifier,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetPerson
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetPerson.universalIdentifier,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetCompany
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetCompany.universalIdentifier,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetOpportunity
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetOpportunity.universalIdentifier,
|
||||
position: 4,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetTask
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetTask.universalIdentifier,
|
||||
position: 5,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetNote
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetNote.universalIdentifier,
|
||||
position: 6,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflow
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetWorkflow.universalIdentifier,
|
||||
position: 7,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowVersion
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetWorkflowVersion.universalIdentifier,
|
||||
position: 8,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowRun
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetWorkflowRun.universalIdentifier,
|
||||
position: 9,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.targetDashboard
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.targetDashboard.universalIdentifier,
|
||||
position: 10,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.workspaceMember
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.workspaceMember.universalIdentifier,
|
||||
position: 11,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.properties
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.properties.universalIdentifier,
|
||||
position: 12,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.fields.linkedRecordCachedName
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.timelineActivity.views.allTimelineActivities
|
||||
.viewFields.linkedRecordCachedName.universalIdentifier,
|
||||
position: 13,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
objectUI: STANDARD_OBJECTS.workspaceMember.universalIdentifier,
|
||||
viewName: 'All Workspace Members',
|
||||
viewIcon: 'IconList',
|
||||
viewUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers
|
||||
.universalIdentifier,
|
||||
viewFields: [
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.name.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.name.universalIdentifier,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.userEmail.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.userEmail.universalIdentifier,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.avatarUrl.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.avatarUrl.universalIdentifier,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.colorScheme
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.colorScheme.universalIdentifier,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.locale.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.locale.universalIdentifier,
|
||||
position: 4,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.timeZone.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.timeZone.universalIdentifier,
|
||||
position: 5,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.dateFormat
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.dateFormat.universalIdentifier,
|
||||
position: 6,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.timeFormat
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.timeFormat.universalIdentifier,
|
||||
position: 7,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.createdAt.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.createdAt.universalIdentifier,
|
||||
position: 8,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.ownedOpportunities
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.ownedOpportunities.universalIdentifier,
|
||||
position: 9,
|
||||
},
|
||||
{
|
||||
fieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.fields.assignedTasks
|
||||
.universalIdentifier,
|
||||
viewFieldUI:
|
||||
STANDARD_OBJECTS.workspaceMember.views.allWorkspaceMembers.viewFields
|
||||
.assignedTasks.universalIdentifier,
|
||||
position: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-18:backfill-standard-views-and-field-metadata',
|
||||
description:
|
||||
'Backfill standard views and fix field metadata (isSystem, labels, icons) for attachment, noteTarget, taskTarget, timelineActivity, workspaceMember',
|
||||
})
|
||||
export class BackfillStandardViewsAndFieldMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const dryRun = options?.dryRun ?? false;
|
||||
|
||||
this.logger.log(
|
||||
`${dryRun ? '[DRY RUN] ' : ''}Backfilling views and field metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (dryRun) {
|
||||
this.logger.log(
|
||||
`[DRY RUN] Would update isSystem, labels/icons, backfill views and viewFields for workspace ${workspaceId}. Skipping.`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
// Part 1: Mark fields as isSystem
|
||||
const systemResult = await queryRunner.query(
|
||||
`UPDATE core."fieldMetadata"
|
||||
SET "isSystem" = true
|
||||
WHERE "workspaceId" = $1
|
||||
AND "universalIdentifier" = ANY($2)
|
||||
AND "isSystem" = false`,
|
||||
[workspaceId, FIELDS_TO_MARK_AS_SYSTEM],
|
||||
);
|
||||
|
||||
const systemCount = systemResult?.[1] ?? 0;
|
||||
|
||||
if (systemCount > 0) {
|
||||
this.logger.log(
|
||||
`Marked ${systemCount} field(s) as isSystem for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Part 1b: Update morph field labels and icons to "Target"
|
||||
const labelResult = await queryRunner.query(
|
||||
`UPDATE core."fieldMetadata"
|
||||
SET label = 'Target', icon = 'IconArrowUpRight'
|
||||
WHERE "workspaceId" = $1
|
||||
AND "universalIdentifier" = ANY($2)
|
||||
AND (label != 'Target' OR icon != 'IconArrowUpRight')`,
|
||||
[workspaceId, MORPH_FIELDS_TO_RELABEL],
|
||||
);
|
||||
|
||||
const labelCount = labelResult?.[1] ?? 0;
|
||||
|
||||
if (labelCount > 0) {
|
||||
this.logger.log(
|
||||
`Relabeled ${labelCount} morph field(s) to "Target" for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Part 2: Resolve applicationId for twenty-standard app
|
||||
const applicationRows = await queryRunner.query(
|
||||
`SELECT id FROM core."application"
|
||||
WHERE "workspaceId" = $1
|
||||
AND "universalIdentifier" = $2`,
|
||||
[workspaceId, TWENTY_STANDARD_APPLICATION.universalIdentifier],
|
||||
);
|
||||
|
||||
if (applicationRows.length === 0) {
|
||||
this.logger.warn(
|
||||
`Twenty Standard Application not found for workspace ${workspaceId}. Skipping view backfill.`,
|
||||
);
|
||||
await queryRunner.commitTransaction();
|
||||
await this.invalidateCaches(workspaceId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const applicationId = applicationRows[0].id;
|
||||
|
||||
// Part 3 & 4: Backfill views and viewFields
|
||||
for (const view of VIEWS_TO_BACKFILL) {
|
||||
// Insert view if it doesn't exist
|
||||
const viewInsertResult = await queryRunner.query(
|
||||
`INSERT INTO core."view" (
|
||||
id, name, "objectMetadataId", type, icon, position,
|
||||
visibility, "workspaceId", "applicationId", "universalIdentifier"
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(), $2, om.id, 'TABLE', $3, 0,
|
||||
'WORKSPACE', $1, $4, $5
|
||||
FROM core."objectMetadata" om
|
||||
WHERE om."workspaceId" = $1
|
||||
AND om."universalIdentifier" = $6
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM core."view" v
|
||||
WHERE v."workspaceId" = $1
|
||||
AND v."universalIdentifier" = $5
|
||||
AND v."deletedAt" IS NULL
|
||||
)
|
||||
RETURNING 1`,
|
||||
[
|
||||
workspaceId,
|
||||
view.viewName,
|
||||
view.viewIcon,
|
||||
applicationId,
|
||||
view.viewUI,
|
||||
view.objectUI,
|
||||
],
|
||||
);
|
||||
|
||||
const viewInserted = viewInsertResult?.length ?? 0;
|
||||
|
||||
if (viewInserted > 0) {
|
||||
this.logger.log(
|
||||
`Created view "${view.viewName}" for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Insert viewFields
|
||||
for (const viewField of view.viewFields) {
|
||||
await queryRunner.query(
|
||||
`INSERT INTO core."viewField" (
|
||||
id, "fieldMetadataId", "isVisible", size, position,
|
||||
"viewId", "workspaceId", "applicationId", "universalIdentifier"
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(), fm.id, true, 150, $4,
|
||||
v.id, $1, $5, $6
|
||||
FROM core."view" v
|
||||
JOIN core."fieldMetadata" fm
|
||||
ON fm."workspaceId" = $1
|
||||
AND fm."universalIdentifier" = $3
|
||||
WHERE v."workspaceId" = $1
|
||||
AND v."universalIdentifier" = $2
|
||||
AND v."deletedAt" IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM core."viewField" vf
|
||||
WHERE vf."workspaceId" = $1
|
||||
AND vf."universalIdentifier" = $6
|
||||
AND vf."deletedAt" IS NULL
|
||||
)`,
|
||||
[
|
||||
workspaceId,
|
||||
view.viewUI,
|
||||
viewField.fieldUI,
|
||||
viewField.position,
|
||||
applicationId,
|
||||
viewField.viewFieldUI,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
this.logger.log(`Completed for workspace ${workspaceId}`);
|
||||
|
||||
await this.invalidateCaches(workspaceId);
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(
|
||||
`Error backfilling views and field metadata (rolled back) for workspace ${workspaceId}`,
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
this.logger.error(
|
||||
`Error backfilling views and field metadata (after commit) for workspace ${workspaceId}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
private async invalidateCaches(workspaceId: string): Promise<void> {
|
||||
const modifiedMetadataNames = [
|
||||
'fieldMetadata',
|
||||
'view',
|
||||
'viewField',
|
||||
] as const;
|
||||
|
||||
const cacheKeysToInvalidate: WorkspaceCacheKeyName[] = [
|
||||
...new Set(
|
||||
modifiedMetadataNames
|
||||
.flatMap((name) => [name, ...getMetadataRelatedMetadataNames(name)])
|
||||
.map(getMetadataFlatEntityMapsKey),
|
||||
),
|
||||
'ORMEntityMetadatas',
|
||||
];
|
||||
|
||||
await this.workspaceCacheService.invalidateAndRecompute(
|
||||
workspaceId,
|
||||
cacheKeysToInvalidate,
|
||||
);
|
||||
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.workspaceCacheStorageService.flush(workspaceId);
|
||||
|
||||
this.logger.log(
|
||||
`Cache invalidated and metadata version incremented for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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 { BackfillStandardViewsAndFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-standard-views-and-field-metadata.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';
|
||||
@@ -15,6 +16,8 @@ import { FilesFieldModule } from 'src/engine/core-modules/file/files-field/files
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
@@ -32,6 +35,8 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
FeatureFlagModule,
|
||||
FileStorageModule.forRoot(),
|
||||
WorkspaceCacheModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
FieldMetadataModule,
|
||||
ApplicationModule,
|
||||
FilesFieldModule,
|
||||
@@ -42,6 +47,7 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
BackfillFileSizeAndMimeTypeCommand,
|
||||
MigrateActivityRichTextAttachmentFileIdsCommand,
|
||||
BackfillMessageChannelThrottleRetryAfterCommand,
|
||||
BackfillStandardViewsAndFieldMetadataCommand,
|
||||
],
|
||||
exports: [
|
||||
MigratePersonAvatarFilesCommand,
|
||||
@@ -49,6 +55,7 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
BackfillFileSizeAndMimeTypeCommand,
|
||||
MigrateActivityRichTextAttachmentFileIdsCommand,
|
||||
BackfillMessageChannelThrottleRetryAfterCommand,
|
||||
BackfillStandardViewsAndFieldMetadataCommand,
|
||||
],
|
||||
})
|
||||
export class V1_18_UpgradeVersionCommandModule {}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/
|
||||
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 { BackfillStandardViewsAndFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-standard-views-and-field-metadata.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';
|
||||
@@ -61,6 +62,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly migrateAttachmentFilesCommand: MigrateAttachmentFilesCommand,
|
||||
protected readonly migrateActivityRichTextAttachmentFileIdsCommand: MigrateActivityRichTextAttachmentFileIdsCommand,
|
||||
protected readonly backfillMessageChannelThrottleRetryAfterCommand: BackfillMessageChannelThrottleRetryAfterCommand,
|
||||
protected readonly backfillStandardViewsAndFieldMetadataCommand: BackfillStandardViewsAndFieldMetadataCommand,
|
||||
) {
|
||||
super(
|
||||
workspaceRepository,
|
||||
@@ -92,6 +94,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
this.migrateActivityRichTextAttachmentFileIdsCommand,
|
||||
this.backfillFileSizeAndMimeTypeCommand,
|
||||
this.backfillMessageChannelThrottleRetryAfterCommand,
|
||||
this.backfillStandardViewsAndFieldMetadataCommand,
|
||||
];
|
||||
|
||||
this.allCommands = {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
const makeMorphField = (
|
||||
overrides: Partial<FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>> & {
|
||||
id: string;
|
||||
morphId: string;
|
||||
},
|
||||
): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION> =>
|
||||
({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
...overrides,
|
||||
}) as FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
|
||||
|
||||
const makeTextField = (id: string): FlatFieldMetadata<FieldMetadataType.TEXT> =>
|
||||
({
|
||||
id,
|
||||
type: FieldMetadataType.TEXT,
|
||||
}) as FlatFieldMetadata<FieldMetadataType.TEXT>;
|
||||
|
||||
describe('filterMorphRelationDuplicateFields', () => {
|
||||
it('should return all fields when there are no morph fields', () => {
|
||||
const fields = [makeTextField('t1'), makeTextField('t2')];
|
||||
|
||||
expect(filterMorphRelationDuplicateFields(fields)).toEqual(fields);
|
||||
});
|
||||
|
||||
it('should return all fields when morph fields have distinct morphIds', () => {
|
||||
const morph1 = makeMorphField({ id: 'a', morphId: 'morph-1' });
|
||||
const morph2 = makeMorphField({ id: 'b', morphId: 'morph-2' });
|
||||
const text = makeTextField('t1');
|
||||
|
||||
const result = filterMorphRelationDuplicateFields([morph1, text, morph2]);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toContain(text);
|
||||
expect(result).toContain(morph1);
|
||||
expect(result).toContain(morph2);
|
||||
});
|
||||
|
||||
it('should deduplicate morph fields sharing the same morphId', () => {
|
||||
const standard = makeMorphField({
|
||||
id: 'b',
|
||||
morphId: 'morph-1',
|
||||
isSystem: false,
|
||||
});
|
||||
const system = makeMorphField({
|
||||
id: 'a',
|
||||
morphId: 'morph-1',
|
||||
isSystem: true,
|
||||
});
|
||||
const text = makeTextField('t1');
|
||||
|
||||
const result = filterMorphRelationDuplicateFields([system, text, standard]);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result).toContain(text);
|
||||
expect(result).toContain(standard);
|
||||
expect(result).not.toContain(system);
|
||||
});
|
||||
|
||||
it('should handle multiple morph groups independently', () => {
|
||||
const group1Best = makeMorphField({
|
||||
id: 'a',
|
||||
morphId: 'morph-1',
|
||||
isSystem: false,
|
||||
});
|
||||
const group1Dup = makeMorphField({
|
||||
id: 'b',
|
||||
morphId: 'morph-1',
|
||||
isSystem: true,
|
||||
});
|
||||
const group2Best = makeMorphField({
|
||||
id: 'c',
|
||||
morphId: 'morph-2',
|
||||
isSystem: false,
|
||||
});
|
||||
const group2Dup = makeMorphField({
|
||||
id: 'd',
|
||||
morphId: 'morph-2',
|
||||
isSystem: true,
|
||||
});
|
||||
|
||||
const result = filterMorphRelationDuplicateFields([
|
||||
group1Dup,
|
||||
group2Dup,
|
||||
group1Best,
|
||||
group2Best,
|
||||
]);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result).toContain(group1Best);
|
||||
expect(result).toContain(group2Best);
|
||||
});
|
||||
|
||||
it('should return empty array for empty input', () => {
|
||||
expect(filterMorphRelationDuplicateFields([])).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { pickMorphGroupSurvivor } from 'src/engine/dataloaders/utils/pick-morph-group-survivor.util';
|
||||
|
||||
const makeMorphField = (
|
||||
overrides: Partial<FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>> & {
|
||||
id: string;
|
||||
},
|
||||
): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION> =>
|
||||
({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
morphId: 'morph-1',
|
||||
...overrides,
|
||||
}) as FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
|
||||
|
||||
describe('pickMorphGroupSurvivor', () => {
|
||||
it('should return the only field when group has one element', () => {
|
||||
const field = makeMorphField({ id: 'a' });
|
||||
|
||||
expect(pickMorphGroupSurvivor([field])).toBe(field);
|
||||
});
|
||||
|
||||
it('should prefer active non-system over active system', () => {
|
||||
const standard = makeMorphField({
|
||||
id: 'b',
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
});
|
||||
const system = makeMorphField({
|
||||
id: 'a',
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
});
|
||||
|
||||
expect(pickMorphGroupSurvivor([system, standard])).toBe(standard);
|
||||
});
|
||||
|
||||
it('should prefer active over inactive', () => {
|
||||
const active = makeMorphField({
|
||||
id: 'b',
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
});
|
||||
const inactive = makeMorphField({
|
||||
id: 'a',
|
||||
isActive: false,
|
||||
isSystem: false,
|
||||
});
|
||||
|
||||
expect(pickMorphGroupSurvivor([inactive, active])).toBe(active);
|
||||
});
|
||||
|
||||
it('should break ties by smallest id', () => {
|
||||
const fieldA = makeMorphField({
|
||||
id: 'aaa',
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
});
|
||||
const fieldB = makeMorphField({
|
||||
id: 'bbb',
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
});
|
||||
|
||||
expect(pickMorphGroupSurvivor([fieldB, fieldA])).toBe(fieldA);
|
||||
});
|
||||
|
||||
it('should prefer active+non-system (score 3) over inactive+non-system (score 1)', () => {
|
||||
const best = makeMorphField({
|
||||
id: 'z',
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
});
|
||||
const worse = makeMorphField({
|
||||
id: 'a',
|
||||
isActive: false,
|
||||
isSystem: false,
|
||||
});
|
||||
|
||||
expect(pickMorphGroupSurvivor([worse, best])).toBe(best);
|
||||
});
|
||||
});
|
||||
@@ -2,52 +2,42 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { pickMorphGroupSurvivor } from 'src/engine/dataloaders/utils/pick-morph-group-survivor.util';
|
||||
|
||||
export const filterMorphRelationDuplicateFields = (
|
||||
flatFieldMetadatas: FlatFieldMetadata[],
|
||||
): FlatFieldMetadata[] => {
|
||||
const initialAccumulator: {
|
||||
morphFlatFieldMetadatas: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[];
|
||||
otherFlatFieldMetadatas: FlatFieldMetadata[];
|
||||
} = {
|
||||
morphFlatFieldMetadatas: [],
|
||||
otherFlatFieldMetadatas: [],
|
||||
};
|
||||
const { morphFlatFieldMetadatas, otherFlatFieldMetadatas } =
|
||||
flatFieldMetadatas.reduce((acc, flatFieldMetadata) => {
|
||||
if (
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
return {
|
||||
...acc,
|
||||
morphFlatFieldMetadatas: [
|
||||
...acc.morphFlatFieldMetadatas,
|
||||
flatFieldMetadata,
|
||||
],
|
||||
};
|
||||
}
|
||||
const otherFlatFieldMetadatas: FlatFieldMetadata[] = [];
|
||||
const morphGroupsByMorphId = new Map<
|
||||
string,
|
||||
FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[]
|
||||
>();
|
||||
|
||||
return {
|
||||
...acc,
|
||||
otherFlatFieldMetadatas: [
|
||||
...acc.otherFlatFieldMetadatas,
|
||||
flatFieldMetadata,
|
||||
],
|
||||
};
|
||||
}, initialAccumulator);
|
||||
for (const flatFieldMetadata of flatFieldMetadatas) {
|
||||
if (
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
const existing =
|
||||
morphGroupsByMorphId.get(flatFieldMetadata.morphId) ?? [];
|
||||
|
||||
const filteredMorphFlatFieldMetadatas = morphFlatFieldMetadatas.filter(
|
||||
(currentField) =>
|
||||
!morphFlatFieldMetadatas.some(
|
||||
(otherField) =>
|
||||
currentField.id !== otherField.id &&
|
||||
otherField.morphId === currentField.morphId &&
|
||||
otherField.id < currentField.id,
|
||||
),
|
||||
);
|
||||
morphGroupsByMorphId.set(flatFieldMetadata.morphId, [
|
||||
...existing,
|
||||
flatFieldMetadata,
|
||||
]);
|
||||
} else {
|
||||
otherFlatFieldMetadatas.push(flatFieldMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
const filteredMorphFlatFieldMetadatas: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] =
|
||||
[];
|
||||
|
||||
for (const group of morphGroupsByMorphId.values()) {
|
||||
filteredMorphFlatFieldMetadatas.push(pickMorphGroupSurvivor(group));
|
||||
}
|
||||
|
||||
return [...otherFlatFieldMetadatas, ...filteredMorphFlatFieldMetadatas];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
// Prefers active non-system fields (standard targets) over system ones
|
||||
// (auto-created for custom objects). Smallest id breaks ties.
|
||||
const scoreMorphField = (
|
||||
field: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>,
|
||||
): number => (field.isActive ? 2 : 0) + (field.isSystem ? 0 : 1);
|
||||
|
||||
export const pickMorphGroupSurvivor = (
|
||||
group: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[],
|
||||
): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION> => {
|
||||
return group.reduce((best, current) => {
|
||||
const diff = scoreMorphField(current) - scoreMorphField(best);
|
||||
|
||||
return diff > 0 || (diff === 0 && current.id < best.id) ? current : best;
|
||||
});
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import { z } from 'zod';
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { formatValidationErrors } from 'src/engine/core-modules/tool-provider/utils/format-validation-errors.util';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import { ViewVisibility } from 'src/engine/metadata-modules/view/enums/view-visibility.enum';
|
||||
import { ViewQueryParamsService } from 'src/engine/metadata-modules/view/services/view-query-params.service';
|
||||
@@ -105,17 +106,19 @@ export class ViewToolsFactory {
|
||||
},
|
||||
);
|
||||
|
||||
const objectMetadata = Object.values(
|
||||
flatObjectMetadataMaps.byUniversalIdentifier,
|
||||
).find((obj) => obj?.nameSingular === objectNameSingular);
|
||||
const { idByNameSingular } = buildObjectIdByNameMaps(
|
||||
flatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
if (!objectMetadata) {
|
||||
const objectMetadataId = idByNameSingular[objectNameSingular];
|
||||
|
||||
if (!objectMetadataId) {
|
||||
throw new Error(
|
||||
`Object "${objectNameSingular}" not found. Use get_object_metadata to list available objects.`,
|
||||
);
|
||||
}
|
||||
|
||||
return objectMetadata.id;
|
||||
return objectMetadataId;
|
||||
}
|
||||
|
||||
private async resolveFieldMetadataId(
|
||||
|
||||
@@ -10,8 +10,7 @@ import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev
|
||||
type AttachmentDataSeed = {
|
||||
id: string;
|
||||
name: string;
|
||||
fullPath: string;
|
||||
fileCategory: string;
|
||||
file: string;
|
||||
createdBySource: string;
|
||||
createdByWorkspaceMemberId: string;
|
||||
createdByName: string;
|
||||
@@ -28,8 +27,7 @@ type AttachmentDataSeed = {
|
||||
export const ATTACHMENT_DATA_SEED_COLUMNS: (keyof AttachmentDataSeed)[] = [
|
||||
'id',
|
||||
'name',
|
||||
'fullPath',
|
||||
'fileCategory',
|
||||
'file',
|
||||
'createdBySource',
|
||||
'createdByWorkspaceMemberId',
|
||||
'createdByName',
|
||||
@@ -58,203 +56,153 @@ const GENERATE_ATTACHMENT_IDS = (): Record<string, string> => {
|
||||
|
||||
export const ATTACHMENT_DATA_SEED_IDS = GENERATE_ATTACHMENT_IDS();
|
||||
|
||||
// Pool of 5 reusable file paths for attachments
|
||||
const FILE_TEMPLATE_PATHS = [
|
||||
'attachment/sample-contract.pdf',
|
||||
'attachment/budget-2024.xlsx',
|
||||
'attachment/presentation.pptx',
|
||||
'attachment/screenshot.png',
|
||||
'attachment/archive.zip',
|
||||
// FileIds must be unique per workspace since core.file is a shared table.
|
||||
// We use the first 12 hex chars of the workspaceId as a namespace suffix.
|
||||
const deriveFileId = (attachmentIndex: number, workspaceId: string): string => {
|
||||
const workspaceHex = workspaceId.replace(/-/g, '').slice(0, 12);
|
||||
const hexIndex = attachmentIndex.toString(16).padStart(4, '0');
|
||||
|
||||
return `f11e0000-${hexIndex}-4a7c-8001-${workspaceHex}`;
|
||||
};
|
||||
|
||||
export const ATTACHMENT_SAMPLE_FILES = [
|
||||
{
|
||||
filename: 'sample-contract.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
extension: 'pdf',
|
||||
},
|
||||
{
|
||||
filename: 'budget-2024.xlsx',
|
||||
mimeType:
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
extension: 'xlsx',
|
||||
},
|
||||
{
|
||||
filename: 'presentation.pptx',
|
||||
mimeType:
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
extension: 'pptx',
|
||||
},
|
||||
{
|
||||
filename: 'screenshot.png',
|
||||
mimeType: 'image/png',
|
||||
extension: 'png',
|
||||
},
|
||||
{
|
||||
filename: 'archive.zip',
|
||||
mimeType: 'application/zip',
|
||||
extension: 'zip',
|
||||
},
|
||||
];
|
||||
|
||||
// Additional name variations for more realistic variety
|
||||
const FILE_NAME_VARIATIONS = [
|
||||
// Documents
|
||||
{
|
||||
name: 'Service Agreement.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'NDA Document.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'Project Proposal.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'Invoice Q1 2024.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'Meeting Notes.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'Report Final.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{
|
||||
name: 'Contract Signed.pdf',
|
||||
fileCategory: 'TEXT_DOCUMENT',
|
||||
pathIndex: 0,
|
||||
},
|
||||
{ name: 'Service Agreement.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'NDA Document.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'Project Proposal.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'Invoice Q1 2024.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'Meeting Notes.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'Report Final.pdf', sampleFileIndex: 0 },
|
||||
{ name: 'Contract Signed.pdf', sampleFileIndex: 0 },
|
||||
// Spreadsheets
|
||||
{
|
||||
name: 'Financial Forecast.xlsx',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{
|
||||
name: 'Sales Report Q4.xlsx',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{
|
||||
name: 'Team Roster.xlsx',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{
|
||||
name: 'Expense Report.xlsx',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{
|
||||
name: 'Inventory List.xlsx',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{
|
||||
name: 'Data Export.csv',
|
||||
fileCategory: 'SPREADSHEET',
|
||||
pathIndex: 1,
|
||||
},
|
||||
{ name: 'Financial Forecast.xlsx', sampleFileIndex: 1 },
|
||||
{ name: 'Sales Report Q4.xlsx', sampleFileIndex: 1 },
|
||||
{ name: 'Team Roster.xlsx', sampleFileIndex: 1 },
|
||||
{ name: 'Expense Report.xlsx', sampleFileIndex: 1 },
|
||||
{ name: 'Inventory List.xlsx', sampleFileIndex: 1 },
|
||||
{ name: 'Data Export.csv', sampleFileIndex: 1 },
|
||||
// Presentations
|
||||
{
|
||||
name: 'Pitch Deck.pptx',
|
||||
fileCategory: 'PRESENTATION',
|
||||
pathIndex: 2,
|
||||
},
|
||||
{
|
||||
name: 'Q4 Results.pptx',
|
||||
fileCategory: 'PRESENTATION',
|
||||
pathIndex: 2,
|
||||
},
|
||||
{
|
||||
name: 'Roadmap 2024.pptx',
|
||||
fileCategory: 'PRESENTATION',
|
||||
pathIndex: 2,
|
||||
},
|
||||
{
|
||||
name: 'Company Overview.pptx',
|
||||
fileCategory: 'PRESENTATION',
|
||||
pathIndex: 2,
|
||||
},
|
||||
{
|
||||
name: 'Training Materials.pptx',
|
||||
fileCategory: 'PRESENTATION',
|
||||
pathIndex: 2,
|
||||
},
|
||||
{ name: 'Pitch Deck.pptx', sampleFileIndex: 2 },
|
||||
{ name: 'Q4 Results.pptx', sampleFileIndex: 2 },
|
||||
{ name: 'Roadmap 2024.pptx', sampleFileIndex: 2 },
|
||||
{ name: 'Company Overview.pptx', sampleFileIndex: 2 },
|
||||
{ name: 'Training Materials.pptx', sampleFileIndex: 2 },
|
||||
// Images
|
||||
{
|
||||
name: 'Company Logo.png',
|
||||
fileCategory: 'IMAGE',
|
||||
pathIndex: 3,
|
||||
},
|
||||
{
|
||||
name: 'Product Photo.jpg',
|
||||
fileCategory: 'IMAGE',
|
||||
pathIndex: 3,
|
||||
},
|
||||
{ name: 'Diagram.png', fileCategory: 'IMAGE', pathIndex: 3 },
|
||||
{ name: 'Wireframe.png', fileCategory: 'IMAGE', pathIndex: 3 },
|
||||
{
|
||||
name: 'Mockup Design.png',
|
||||
fileCategory: 'IMAGE',
|
||||
pathIndex: 3,
|
||||
},
|
||||
{ name: 'Headshot.jpg', fileCategory: 'IMAGE', pathIndex: 3 },
|
||||
{ name: 'Company Logo.png', sampleFileIndex: 3 },
|
||||
{ name: 'Product Photo.jpg', sampleFileIndex: 3 },
|
||||
{ name: 'Diagram.png', sampleFileIndex: 3 },
|
||||
{ name: 'Wireframe.png', sampleFileIndex: 3 },
|
||||
{ name: 'Mockup Design.png', sampleFileIndex: 3 },
|
||||
{ name: 'Headshot.jpg', sampleFileIndex: 3 },
|
||||
// Archives
|
||||
{
|
||||
name: 'Project Files.zip',
|
||||
fileCategory: 'ARCHIVE',
|
||||
pathIndex: 4,
|
||||
},
|
||||
{
|
||||
name: 'Backup Data.zip',
|
||||
fileCategory: 'ARCHIVE',
|
||||
pathIndex: 4,
|
||||
},
|
||||
{
|
||||
name: 'Source Code.zip',
|
||||
fileCategory: 'ARCHIVE',
|
||||
pathIndex: 4,
|
||||
},
|
||||
{ name: 'Project Files.zip', sampleFileIndex: 4 },
|
||||
{ name: 'Backup Data.zip', sampleFileIndex: 4 },
|
||||
{ name: 'Source Code.zip', sampleFileIndex: 4 },
|
||||
];
|
||||
|
||||
const GENERATE_ATTACHMENT_SEEDS = (): AttachmentDataSeed[] => {
|
||||
const ATTACHMENT_SEEDS: AttachmentDataSeed[] = [];
|
||||
export type AttachmentFileSeedMetadata = {
|
||||
fileId: string;
|
||||
label: string;
|
||||
sampleFileIndex: number;
|
||||
mimeType: string;
|
||||
extension: string;
|
||||
};
|
||||
|
||||
// Get available entity IDs
|
||||
const PERSON_IDS = Object.values(PERSON_DATA_SEED_IDS).slice(0, 120); // Use first 120 persons
|
||||
const COMPANY_IDS = Object.values(COMPANY_DATA_SEED_IDS).slice(0, 120); // Use first 120 companies
|
||||
const NOTE_IDS = Object.values(NOTE_DATA_SEED_IDS).slice(0, 80); // Use first 80 notes
|
||||
const TASK_IDS = Object.values(TASK_DATA_SEED_IDS).slice(0, 60); // Use first 60 tasks
|
||||
const OPPORTUNITY_IDS = Object.values(OPPORTUNITY_DATA_SEED_IDS).slice(0, 20); // Use first 20 opportunities
|
||||
export const generateAttachmentSeedsForWorkspace = (
|
||||
workspaceId: string,
|
||||
): {
|
||||
seeds: AttachmentDataSeed[];
|
||||
fileSeedMetadata: AttachmentFileSeedMetadata[];
|
||||
} => {
|
||||
const seeds: AttachmentDataSeed[] = [];
|
||||
const fileSeedMetadata: AttachmentFileSeedMetadata[] = [];
|
||||
|
||||
const PERSON_IDS = Object.values(PERSON_DATA_SEED_IDS).slice(0, 120);
|
||||
const COMPANY_IDS = Object.values(COMPANY_DATA_SEED_IDS).slice(0, 120);
|
||||
const NOTE_IDS = Object.values(NOTE_DATA_SEED_IDS).slice(0, 80);
|
||||
const TASK_IDS = Object.values(TASK_DATA_SEED_IDS).slice(0, 60);
|
||||
const OPPORTUNITY_IDS = Object.values(OPPORTUNITY_DATA_SEED_IDS).slice(0, 20);
|
||||
|
||||
let entityIndex = 0;
|
||||
|
||||
for (let INDEX = 1; INDEX <= 400; INDEX++) {
|
||||
// Cycle through file name variations
|
||||
const NAME_VARIATION_INDEX = INDEX % FILE_NAME_VARIATIONS.length;
|
||||
const NAME_VARIATION = FILE_NAME_VARIATIONS[NAME_VARIATION_INDEX];
|
||||
const FILE_PATH = FILE_TEMPLATE_PATHS[NAME_VARIATION.pathIndex];
|
||||
for (let index = 1; index <= 400; index++) {
|
||||
const nameVariationIndex = index % FILE_NAME_VARIATIONS.length;
|
||||
const nameVariation = FILE_NAME_VARIATIONS[nameVariationIndex];
|
||||
const sampleFile = ATTACHMENT_SAMPLE_FILES[nameVariation.sampleFileIndex];
|
||||
|
||||
const attachmentId = ATTACHMENT_DATA_SEED_IDS[`ID_${index}`];
|
||||
const fileId = deriveFileId(index, workspaceId);
|
||||
|
||||
// Determine which entity this attachment belongs to
|
||||
// Distribution: ~30% person, ~30% company, ~20% note, ~15% task, ~5% opportunity
|
||||
let targetPersonId: string | null = null;
|
||||
let targetCompanyId: string | null = null;
|
||||
let targetNoteId: string | null = null;
|
||||
let targetTaskId: string | null = null;
|
||||
let targetOpportunityId: string | null = null;
|
||||
|
||||
const DISTRIBUTION_VALUE = INDEX % 100;
|
||||
const distributionValue = index % 100;
|
||||
|
||||
if (DISTRIBUTION_VALUE < 30) {
|
||||
// 30% Person attachments
|
||||
if (distributionValue < 30) {
|
||||
targetPersonId = PERSON_IDS[entityIndex % PERSON_IDS.length];
|
||||
entityIndex++;
|
||||
} else if (DISTRIBUTION_VALUE < 60) {
|
||||
// 30% Company attachments
|
||||
} else if (distributionValue < 60) {
|
||||
targetCompanyId = COMPANY_IDS[entityIndex % COMPANY_IDS.length];
|
||||
entityIndex++;
|
||||
} else if (DISTRIBUTION_VALUE < 80) {
|
||||
// 20% Note attachments
|
||||
} else if (distributionValue < 80) {
|
||||
targetNoteId = NOTE_IDS[entityIndex % NOTE_IDS.length];
|
||||
entityIndex++;
|
||||
} else if (DISTRIBUTION_VALUE < 95) {
|
||||
// 15% Task attachments
|
||||
} else if (distributionValue < 95) {
|
||||
targetTaskId = TASK_IDS[entityIndex % TASK_IDS.length];
|
||||
entityIndex++;
|
||||
} else {
|
||||
// 5% Opportunity attachments
|
||||
targetOpportunityId =
|
||||
OPPORTUNITY_IDS[entityIndex % OPPORTUNITY_IDS.length];
|
||||
entityIndex++;
|
||||
}
|
||||
|
||||
ATTACHMENT_SEEDS.push({
|
||||
id: ATTACHMENT_DATA_SEED_IDS[`ID_${INDEX}`],
|
||||
name: NAME_VARIATION.name,
|
||||
fullPath: FILE_PATH,
|
||||
fileCategory: NAME_VARIATION.fileCategory,
|
||||
fileSeedMetadata.push({
|
||||
fileId,
|
||||
label: nameVariation.name,
|
||||
sampleFileIndex: nameVariation.sampleFileIndex,
|
||||
mimeType: sampleFile.mimeType,
|
||||
extension: sampleFile.extension,
|
||||
});
|
||||
|
||||
seeds.push({
|
||||
id: attachmentId,
|
||||
name: nameVariation.name,
|
||||
file: JSON.stringify([
|
||||
{ fileId, label: nameVariation.name, extension: sampleFile.extension },
|
||||
]),
|
||||
createdBySource: FieldActorSource.MANUAL,
|
||||
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
|
||||
createdByName: 'Tim A',
|
||||
@@ -269,7 +217,5 @@ const GENERATE_ATTACHMENT_SEEDS = (): AttachmentDataSeed[] => {
|
||||
});
|
||||
}
|
||||
|
||||
return ATTACHMENT_SEEDS;
|
||||
return { seeds, fileSeedMetadata };
|
||||
};
|
||||
|
||||
export const ATTACHMENT_DATA_SEEDS = GENERATE_ATTACHMENT_SEEDS();
|
||||
|
||||
@@ -4,6 +4,8 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
@@ -15,7 +17,9 @@ import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manage
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import {
|
||||
ATTACHMENT_DATA_SEED_COLUMNS,
|
||||
ATTACHMENT_DATA_SEEDS,
|
||||
ATTACHMENT_SAMPLE_FILES,
|
||||
type AttachmentFileSeedMetadata,
|
||||
generateAttachmentSeedsForWorkspace,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/attachment-data-seeds.constant';
|
||||
import {
|
||||
CALENDAR_CHANNEL_DATA_SEED_COLUMNS,
|
||||
@@ -115,6 +119,7 @@ import {
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import { TimelineActivitySeederService } from 'src/engine/workspace-manager/dev-seeder/data/services/timeline-activity-seeder.service';
|
||||
import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
|
||||
type RecordSeedConfig = {
|
||||
tableName: string;
|
||||
@@ -125,6 +130,7 @@ type RecordSeedConfig = {
|
||||
// Organize seeds into dependency batches for parallel insertion
|
||||
const getRecordSeedsBatches = (
|
||||
workspaceId: string,
|
||||
attachmentSeeds: RecordSeedConfig['recordSeeds'],
|
||||
_featureFlags?: Record<FeatureFlagKey, boolean>,
|
||||
): RecordSeedConfig[][] => {
|
||||
// Batch 1: No dependencies
|
||||
@@ -273,7 +279,7 @@ const getRecordSeedsBatches = (
|
||||
{
|
||||
tableName: 'attachment',
|
||||
pgColumns: ATTACHMENT_DATA_SEED_COLUMNS,
|
||||
recordSeeds: ATTACHMENT_DATA_SEEDS,
|
||||
recordSeeds: attachmentSeeds,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -311,12 +317,16 @@ export class DevSeederDataService {
|
||||
},
|
||||
);
|
||||
|
||||
const { seeds: attachmentSeeds, fileSeedMetadata: attachmentFileMeta } =
|
||||
generateAttachmentSeedsForWorkspace(workspaceId);
|
||||
|
||||
await this.coreDataSource.transaction(
|
||||
async (entityManager: WorkspaceEntityManager) => {
|
||||
await this.seedRecordsInBatches({
|
||||
entityManager,
|
||||
schemaName,
|
||||
workspaceId,
|
||||
attachmentSeeds,
|
||||
featureFlags,
|
||||
objectMetadataItems,
|
||||
});
|
||||
@@ -327,7 +337,11 @@ export class DevSeederDataService {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.seedAttachmentFiles(workspaceId);
|
||||
await this.seedAttachmentFiles(
|
||||
workspaceId,
|
||||
entityManager,
|
||||
attachmentFileMeta,
|
||||
);
|
||||
|
||||
await prefillWorkflows(
|
||||
entityManager,
|
||||
@@ -343,16 +357,22 @@ export class DevSeederDataService {
|
||||
entityManager,
|
||||
schemaName,
|
||||
workspaceId,
|
||||
attachmentSeeds,
|
||||
featureFlags,
|
||||
objectMetadataItems,
|
||||
}: {
|
||||
entityManager: WorkspaceEntityManager;
|
||||
schemaName: string;
|
||||
workspaceId: string;
|
||||
attachmentSeeds: RecordSeedConfig['recordSeeds'];
|
||||
featureFlags?: Record<FeatureFlagKey, boolean>;
|
||||
objectMetadataItems: FlatObjectMetadata[];
|
||||
}) {
|
||||
const batches = getRecordSeedsBatches(workspaceId, featureFlags);
|
||||
const batches = getRecordSeedsBatches(
|
||||
workspaceId,
|
||||
attachmentSeeds,
|
||||
featureFlags,
|
||||
);
|
||||
|
||||
// Process batches sequentially (respecting dependencies)
|
||||
// but entities within each batch in parallel
|
||||
@@ -406,9 +426,11 @@ export class DevSeederDataService {
|
||||
.execute();
|
||||
}
|
||||
|
||||
private async seedAttachmentFiles(workspaceId: string): Promise<void> {
|
||||
// Files are copied to dist/assets during build via nest-cli.json
|
||||
// The pattern **/dev-seeder/data/sample-files/** preserves the full path
|
||||
private async seedAttachmentFiles(
|
||||
workspaceId: string,
|
||||
entityManager: WorkspaceEntityManager,
|
||||
fileSeedMetadata: AttachmentFileSeedMetadata[],
|
||||
): Promise<void> {
|
||||
const IS_BUILT = __dirname.includes('/dist/');
|
||||
const sampleFilesDir = IS_BUILT
|
||||
? join(
|
||||
@@ -417,37 +439,38 @@ export class DevSeederDataService {
|
||||
)
|
||||
: join(__dirname, '../sample-files');
|
||||
|
||||
const filesToCreate = [
|
||||
'sample-contract.pdf',
|
||||
'budget-2024.xlsx',
|
||||
'presentation.pptx',
|
||||
'screenshot.png',
|
||||
'archive.zip',
|
||||
];
|
||||
// Read each sample file once and cache the buffer
|
||||
const sampleFileBuffers: Buffer[] = [];
|
||||
|
||||
for (const filename of filesToCreate) {
|
||||
const filePath = join(sampleFilesDir, filename);
|
||||
const fileBuffer = await readFile(filePath);
|
||||
for (const sampleFile of ATTACHMENT_SAMPLE_FILES) {
|
||||
const filePath = join(sampleFilesDir, sampleFile.filename);
|
||||
|
||||
await this.fileStorageService.writeFileLegacy({
|
||||
file: fileBuffer,
|
||||
name: filename,
|
||||
folder: `workspace-${workspaceId}/attachment`,
|
||||
mimeType: this.getMimeType(filename),
|
||||
sampleFileBuffers.push(await readFile(filePath));
|
||||
}
|
||||
|
||||
const fieldUniversalIdentifier =
|
||||
STANDARD_OBJECTS.attachment.fields.file.universalIdentifier;
|
||||
const applicationUniversalIdentifier =
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier;
|
||||
|
||||
for (const metadata of fileSeedMetadata) {
|
||||
const resourcePath = `${metadata.fileId}.${metadata.extension}`;
|
||||
const sourceFile = sampleFileBuffers[metadata.sampleFileIndex];
|
||||
|
||||
await this.fileStorageService.writeFile({
|
||||
sourceFile,
|
||||
mimeType: metadata.mimeType,
|
||||
fileFolder: FileFolder.FilesField,
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
resourcePath: `${fieldUniversalIdentifier}/${resourcePath}`,
|
||||
fileId: metadata.fileId,
|
||||
settings: {
|
||||
isTemporaryFile: false,
|
||||
toDelete: false,
|
||||
},
|
||||
queryRunner: entityManager.queryRunner,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getMimeType(filename: string): string {
|
||||
const ext = filename.split('.').pop()?.toLowerCase();
|
||||
const mimeTypes: Record<string, string> = {
|
||||
pdf: 'application/pdf',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
png: 'image/png',
|
||||
zip: 'application/zip',
|
||||
};
|
||||
|
||||
return mimeTypes[ext || ''] || 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -156,6 +156,7 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
label: 'Full path',
|
||||
description: 'Attachment full path',
|
||||
icon: 'IconLink',
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
},
|
||||
@@ -174,6 +175,7 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
label: 'File category',
|
||||
description: 'Attachment file category',
|
||||
icon: 'IconList',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
defaultValue: "'OTHER'",
|
||||
@@ -261,9 +263,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetTask',
|
||||
label: 'Task',
|
||||
description: 'Attachment task',
|
||||
icon: 'IconNotes',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'task',
|
||||
@@ -286,9 +288,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetNote',
|
||||
label: 'Note',
|
||||
description: 'Attachment note',
|
||||
icon: 'IconNotes',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'note',
|
||||
@@ -311,9 +313,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetPerson',
|
||||
label: 'Person',
|
||||
description: 'Attachment person',
|
||||
icon: 'IconUser',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'person',
|
||||
@@ -336,9 +338,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetCompany',
|
||||
label: 'Company',
|
||||
description: 'Attachment company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'company',
|
||||
@@ -361,9 +363,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetOpportunity',
|
||||
label: 'Opportunity',
|
||||
description: 'Attachment opportunity',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'opportunity',
|
||||
@@ -386,9 +388,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetDashboard',
|
||||
label: 'Dashboard',
|
||||
description: 'Attachment dashboard',
|
||||
icon: 'IconLayout',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'dashboard',
|
||||
@@ -411,9 +413,9 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetWorkflow',
|
||||
label: 'Workflow',
|
||||
description: 'Attachment workflow',
|
||||
icon: 'IconSettingsAutomation',
|
||||
label: 'Target',
|
||||
description: 'Attachment target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'workflow',
|
||||
|
||||
@@ -141,9 +141,9 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetPerson',
|
||||
label: 'Person',
|
||||
description: 'NoteTarget person',
|
||||
icon: 'IconUser',
|
||||
label: 'Target',
|
||||
description: 'NoteTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'person',
|
||||
@@ -166,9 +166,9 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetCompany',
|
||||
label: 'Company',
|
||||
description: 'NoteTarget company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
label: 'Target',
|
||||
description: 'NoteTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'company',
|
||||
@@ -191,9 +191,9 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetOpportunity',
|
||||
label: 'Opportunity',
|
||||
description: 'NoteTarget opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'NoteTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'opportunity',
|
||||
|
||||
@@ -141,9 +141,9 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetPerson',
|
||||
label: 'Person',
|
||||
description: 'TaskTarget person',
|
||||
icon: 'IconUser',
|
||||
label: 'Target',
|
||||
description: 'TaskTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'person',
|
||||
@@ -166,9 +166,9 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetCompany',
|
||||
label: 'Company',
|
||||
description: 'TaskTarget company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
label: 'Target',
|
||||
description: 'TaskTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'company',
|
||||
@@ -191,9 +191,9 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetOpportunity',
|
||||
label: 'Opportunity',
|
||||
description: 'TaskTarget opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'TaskTarget target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'opportunity',
|
||||
|
||||
@@ -167,6 +167,7 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
label: 'Linked Record cached name',
|
||||
description: 'Cached record name',
|
||||
icon: 'IconAbc',
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
},
|
||||
@@ -184,6 +185,7 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
label: 'Linked Record id',
|
||||
description: 'Linked Record id',
|
||||
icon: 'IconAbc',
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
},
|
||||
@@ -201,6 +203,7 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
label: 'Linked Object Metadata Id',
|
||||
description: 'Linked Object Metadata Id',
|
||||
icon: 'IconAbc',
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
},
|
||||
@@ -243,9 +246,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetPerson',
|
||||
label: 'Person',
|
||||
description: 'Event person',
|
||||
icon: 'IconUser',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'person',
|
||||
@@ -268,9 +271,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetCompany',
|
||||
label: 'Company',
|
||||
description: 'Event company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'company',
|
||||
@@ -293,9 +296,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetOpportunity',
|
||||
label: 'Opportunity',
|
||||
description: 'Event opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'opportunity',
|
||||
@@ -318,9 +321,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetNote',
|
||||
label: 'Note',
|
||||
description: 'Event note',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'note',
|
||||
@@ -343,9 +346,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetTask',
|
||||
label: 'Task',
|
||||
description: 'Event task',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'task',
|
||||
@@ -368,9 +371,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetWorkflow',
|
||||
label: 'Workflow',
|
||||
description: 'Event workflow',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'workflow',
|
||||
@@ -393,9 +396,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetWorkflowVersion',
|
||||
label: 'WorkflowVersion',
|
||||
description: 'Event workflow version',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'workflowVersion',
|
||||
@@ -418,9 +421,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetWorkflowRun',
|
||||
label: 'Workflow Run',
|
||||
description: 'Event workflow run',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'workflowRun',
|
||||
@@ -443,9 +446,9 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId,
|
||||
fieldName: 'targetDashboard',
|
||||
label: 'Dashboard',
|
||||
description: 'Event dashboard',
|
||||
icon: 'IconTargetArrow',
|
||||
label: 'Target',
|
||||
description: 'Event target',
|
||||
icon: 'IconArrowUpRight',
|
||||
isNullable: true,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'dashboard',
|
||||
|
||||
@@ -444,6 +444,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Favorites',
|
||||
description: 'Favorites linked to the workspace member',
|
||||
icon: 'IconHeart',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'favorite',
|
||||
@@ -467,6 +468,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Account Owner For Companies',
|
||||
description: 'Account owner for companies',
|
||||
icon: 'IconBriefcase',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'company',
|
||||
@@ -490,6 +492,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Connected accounts',
|
||||
description: 'Connected accounts',
|
||||
icon: 'IconAt',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'connectedAccount',
|
||||
@@ -513,6 +516,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Message Participants',
|
||||
description: 'Message Participants',
|
||||
icon: 'IconUserCircle',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'messageParticipant',
|
||||
@@ -536,6 +540,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Blocklist',
|
||||
description: 'Blocklisted handles',
|
||||
icon: 'IconForbid2',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'blocklist',
|
||||
@@ -559,6 +564,7 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
|
||||
label: 'Calendar Event Participants',
|
||||
description: 'Calendar Event Participants',
|
||||
icon: 'IconCalendar',
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUIReadOnly: true,
|
||||
targetObjectName: 'calendarEventParticipant',
|
||||
|
||||
@@ -5,18 +5,23 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
import { computeStandardAttachmentViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-attachment-view-fields.util';
|
||||
import { computeStandardCalendarEventViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-calendar-event-view-fields.util';
|
||||
import { computeStandardCompanyViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-company-view-fields.util';
|
||||
import { computeStandardDashboardViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-dashboard-view-fields.util';
|
||||
import { computeStandardMessageThreadViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-thread-view-fields.util';
|
||||
import { computeStandardMessageViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-message-view-fields.util';
|
||||
import { computeStandardNoteTargetViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-note-target-view-fields.util';
|
||||
import { computeStandardNoteViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-note-view-fields.util';
|
||||
import { computeStandardOpportunityViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-opportunity-view-fields.util';
|
||||
import { computeStandardPersonViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-person-view-fields.util';
|
||||
import { computeStandardTaskTargetViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-task-target-view-fields.util';
|
||||
import { computeStandardTaskViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-task-view-fields.util';
|
||||
import { computeStandardTimelineActivityViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-timeline-activity-view-fields.util';
|
||||
import { computeStandardWorkflowRunViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-workflow-run-view-fields.util';
|
||||
import { computeStandardWorkflowVersionViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-workflow-version-view-fields.util';
|
||||
import { computeStandardWorkflowViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-workflow-view-fields.util';
|
||||
import { computeStandardWorkspaceMemberViewFields } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/compute-standard-workspace-member-view-fields.util';
|
||||
import { type CreateStandardViewFieldArgs } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
type StandardViewFieldBuilder<P extends AllStandardObjectName> = (
|
||||
@@ -24,18 +29,23 @@ type StandardViewFieldBuilder<P extends AllStandardObjectName> = (
|
||||
) => Record<string, FlatViewField>;
|
||||
|
||||
const STANDARD_FLAT_VIEW_FIELD_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
attachment: computeStandardAttachmentViewFields,
|
||||
calendarEvent: computeStandardCalendarEventViewFields,
|
||||
company: computeStandardCompanyViewFields,
|
||||
dashboard: computeStandardDashboardViewFields,
|
||||
message: computeStandardMessageViewFields,
|
||||
messageThread: computeStandardMessageThreadViewFields,
|
||||
note: computeStandardNoteViewFields,
|
||||
noteTarget: computeStandardNoteTargetViewFields,
|
||||
opportunity: computeStandardOpportunityViewFields,
|
||||
person: computeStandardPersonViewFields,
|
||||
task: computeStandardTaskViewFields,
|
||||
taskTarget: computeStandardTaskTargetViewFields,
|
||||
timelineActivity: computeStandardTimelineActivityViewFields,
|
||||
workflow: computeStandardWorkflowViewFields,
|
||||
workflowRun: computeStandardWorkflowRunViewFields,
|
||||
workflowVersion: computeStandardWorkflowVersionViewFields,
|
||||
workspaceMember: computeStandardWorkspaceMemberViewFields,
|
||||
} as const satisfies {
|
||||
[P in AllStandardObjectName]?: StandardViewFieldBuilder<P>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import {
|
||||
createStandardViewFieldFlatMetadata,
|
||||
type CreateStandardViewFieldArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
export const computeStandardAttachmentViewFields = (
|
||||
args: Omit<CreateStandardViewFieldArgs<'attachment'>, 'context'>,
|
||||
): Record<string, FlatViewField> => {
|
||||
return {
|
||||
allAttachmentsName: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'name',
|
||||
fieldName: 'name',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
}),
|
||||
allAttachmentsFile: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'file',
|
||||
fieldName: 'file',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
// All morph targets are included so the surviving field after dedup always has a viewField
|
||||
allAttachmentsTargetPerson: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetPerson',
|
||||
fieldName: 'targetPerson',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetCompany: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetCompany',
|
||||
fieldName: 'targetCompany',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetOpportunity: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetOpportunity',
|
||||
fieldName: 'targetOpportunity',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetTask: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetTask',
|
||||
fieldName: 'targetTask',
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetNote: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetNote',
|
||||
fieldName: 'targetNote',
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetDashboard: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetDashboard',
|
||||
fieldName: 'targetDashboard',
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsTargetWorkflow: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'targetWorkflow',
|
||||
fieldName: 'targetWorkflow',
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsCreatedBy: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'createdBy',
|
||||
fieldName: 'createdBy',
|
||||
position: 9,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allAttachmentsCreatedAt: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
viewFieldName: 'createdAt',
|
||||
fieldName: 'createdAt',
|
||||
position: 10,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import {
|
||||
createStandardViewFieldFlatMetadata,
|
||||
type CreateStandardViewFieldArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
export const computeStandardNoteTargetViewFields = (
|
||||
args: Omit<CreateStandardViewFieldArgs<'noteTarget'>, 'context'>,
|
||||
): Record<string, FlatViewField> => {
|
||||
return {
|
||||
// Label identifier for junction tables
|
||||
allNoteTargetsId: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
viewFieldName: 'id',
|
||||
fieldName: 'id',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
}),
|
||||
allNoteTargetsNote: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
viewFieldName: 'note',
|
||||
fieldName: 'note',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
// All morph targets are included so the surviving field after dedup always has a viewField
|
||||
allNoteTargetsTargetPerson: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
viewFieldName: 'targetPerson',
|
||||
fieldName: 'targetPerson',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allNoteTargetsTargetCompany: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
viewFieldName: 'targetCompany',
|
||||
fieldName: 'targetCompany',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allNoteTargetsTargetOpportunity: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
viewFieldName: 'targetOpportunity',
|
||||
fieldName: 'targetOpportunity',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import {
|
||||
createStandardViewFieldFlatMetadata,
|
||||
type CreateStandardViewFieldArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
export const computeStandardTaskTargetViewFields = (
|
||||
args: Omit<CreateStandardViewFieldArgs<'taskTarget'>, 'context'>,
|
||||
): Record<string, FlatViewField> => {
|
||||
return {
|
||||
// Label identifier for junction tables
|
||||
allTaskTargetsId: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
viewFieldName: 'id',
|
||||
fieldName: 'id',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
}),
|
||||
allTaskTargetsTask: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
viewFieldName: 'task',
|
||||
fieldName: 'task',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
// All morph targets are included so the surviving field after dedup always has a viewField
|
||||
allTaskTargetsTargetPerson: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
viewFieldName: 'targetPerson',
|
||||
fieldName: 'targetPerson',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTaskTargetsTargetCompany: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
viewFieldName: 'targetCompany',
|
||||
fieldName: 'targetCompany',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTaskTargetsTargetOpportunity: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
viewFieldName: 'targetOpportunity',
|
||||
fieldName: 'targetOpportunity',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import {
|
||||
createStandardViewFieldFlatMetadata,
|
||||
type CreateStandardViewFieldArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
export const computeStandardTimelineActivityViewFields = (
|
||||
args: Omit<CreateStandardViewFieldArgs<'timelineActivity'>, 'context'>,
|
||||
): Record<string, FlatViewField> => {
|
||||
return {
|
||||
allTimelineActivitiesName: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'name',
|
||||
fieldName: 'name',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesHappensAt: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'happensAt',
|
||||
fieldName: 'happensAt',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
// All morph targets are included so the surviving field after dedup always has a viewField
|
||||
allTimelineActivitiesTargetPerson: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetPerson',
|
||||
fieldName: 'targetPerson',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetCompany: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetCompany',
|
||||
fieldName: 'targetCompany',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetOpportunity: createStandardViewFieldFlatMetadata(
|
||||
{
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetOpportunity',
|
||||
fieldName: 'targetOpportunity',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
},
|
||||
),
|
||||
allTimelineActivitiesTargetTask: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetTask',
|
||||
fieldName: 'targetTask',
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetNote: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetNote',
|
||||
fieldName: 'targetNote',
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetWorkflow: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetWorkflow',
|
||||
fieldName: 'targetWorkflow',
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetWorkflowVersion:
|
||||
createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetWorkflowVersion',
|
||||
fieldName: 'targetWorkflowVersion',
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesTargetWorkflowRun: createStandardViewFieldFlatMetadata(
|
||||
{
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetWorkflowRun',
|
||||
fieldName: 'targetWorkflowRun',
|
||||
position: 9,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
},
|
||||
),
|
||||
allTimelineActivitiesTargetDashboard: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'targetDashboard',
|
||||
fieldName: 'targetDashboard',
|
||||
position: 10,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allTimelineActivitiesWorkspaceMember: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
viewFieldName: 'workspaceMember',
|
||||
fieldName: 'workspaceMember',
|
||||
position: 11,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import {
|
||||
createStandardViewFieldFlatMetadata,
|
||||
type CreateStandardViewFieldArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/create-standard-view-field-flat-metadata.util';
|
||||
|
||||
export const computeStandardWorkspaceMemberViewFields = (
|
||||
args: Omit<CreateStandardViewFieldArgs<'workspaceMember'>, 'context'>,
|
||||
): Record<string, FlatViewField> => {
|
||||
return {
|
||||
allWorkspaceMembersName: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'workspaceMember',
|
||||
context: {
|
||||
viewName: 'allWorkspaceMembers',
|
||||
viewFieldName: 'name',
|
||||
fieldName: 'name',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
}),
|
||||
allWorkspaceMembersCreatedAt: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'workspaceMember',
|
||||
context: {
|
||||
viewName: 'allWorkspaceMembers',
|
||||
viewFieldName: 'createdAt',
|
||||
fieldName: 'createdAt',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allWorkspaceMembersOwnedOpportunities: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'workspaceMember',
|
||||
context: {
|
||||
viewName: 'allWorkspaceMembers',
|
||||
viewFieldName: 'ownedOpportunities',
|
||||
fieldName: 'ownedOpportunities',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
allWorkspaceMembersAssignedTasks: createStandardViewFieldFlatMetadata({
|
||||
...args,
|
||||
objectName: 'workspaceMember',
|
||||
context: {
|
||||
viewName: 'allWorkspaceMembers',
|
||||
viewFieldName: 'assignedTasks',
|
||||
fieldName: 'assignedTasks',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -4,18 +4,23 @@ import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-module
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
import { computeStandardAttachmentViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-attachment-views.util';
|
||||
import { computeStandardCalendarEventViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-calendar-event-views.util';
|
||||
import { computeStandardCompanyViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-company-views.util';
|
||||
import { computeStandardDashboardViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-dashboard-views.util';
|
||||
import { computeStandardMessageThreadViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-thread-views.util';
|
||||
import { computeStandardMessageViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-message-views.util';
|
||||
import { computeStandardNoteTargetViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-note-target-views.util';
|
||||
import { computeStandardNoteViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-note-views.util';
|
||||
import { computeStandardOpportunityViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-opportunity-views.util';
|
||||
import { computeStandardPersonViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-person-views.util';
|
||||
import { computeStandardTaskTargetViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-task-target-views.util';
|
||||
import { computeStandardTaskViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-task-views.util';
|
||||
import { computeStandardTimelineActivityViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-timeline-activity-views.util';
|
||||
import { computeStandardWorkflowRunViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-workflow-run-views.util';
|
||||
import { computeStandardWorkflowVersionViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-workflow-version-views.util';
|
||||
import { computeStandardWorkflowViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-workflow-views.util';
|
||||
import { computeStandardWorkspaceMemberViews } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/compute-standard-workspace-member-views.util';
|
||||
import { type CreateStandardViewArgs } from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
type StandardViewBuilder<P extends AllStandardObjectName> = (
|
||||
@@ -23,18 +28,23 @@ type StandardViewBuilder<P extends AllStandardObjectName> = (
|
||||
) => Record<string, FlatView>;
|
||||
|
||||
const STANDARD_FLAT_VIEW_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
attachment: computeStandardAttachmentViews,
|
||||
calendarEvent: computeStandardCalendarEventViews,
|
||||
company: computeStandardCompanyViews,
|
||||
dashboard: computeStandardDashboardViews,
|
||||
message: computeStandardMessageViews,
|
||||
messageThread: computeStandardMessageThreadViews,
|
||||
note: computeStandardNoteViews,
|
||||
noteTarget: computeStandardNoteTargetViews,
|
||||
opportunity: computeStandardOpportunityViews,
|
||||
person: computeStandardPersonViews,
|
||||
task: computeStandardTaskViews,
|
||||
taskTarget: computeStandardTaskTargetViews,
|
||||
timelineActivity: computeStandardTimelineActivityViews,
|
||||
workflow: computeStandardWorkflowViews,
|
||||
workflowRun: computeStandardWorkflowRunViews,
|
||||
workflowVersion: computeStandardWorkflowVersionViews,
|
||||
workspaceMember: computeStandardWorkspaceMemberViews,
|
||||
} as const satisfies {
|
||||
[P in AllStandardObjectName]?: StandardViewBuilder<P>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
export const computeStandardAttachmentViews = (
|
||||
args: Omit<CreateStandardViewArgs<'attachment'>, 'context'>,
|
||||
): Record<string, FlatView> => {
|
||||
return {
|
||||
allAttachments: createStandardViewFlatMetadata({
|
||||
...args,
|
||||
objectName: 'attachment',
|
||||
context: {
|
||||
viewName: 'allAttachments',
|
||||
name: 'All Attachments',
|
||||
type: ViewType.TABLE,
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
export const computeStandardNoteTargetViews = (
|
||||
args: Omit<CreateStandardViewArgs<'noteTarget'>, 'context'>,
|
||||
): Record<string, FlatView> => {
|
||||
return {
|
||||
allNoteTargets: createStandardViewFlatMetadata({
|
||||
...args,
|
||||
objectName: 'noteTarget',
|
||||
context: {
|
||||
viewName: 'allNoteTargets',
|
||||
name: 'All Note Targets',
|
||||
type: ViewType.TABLE,
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
export const computeStandardTaskTargetViews = (
|
||||
args: Omit<CreateStandardViewArgs<'taskTarget'>, 'context'>,
|
||||
): Record<string, FlatView> => {
|
||||
return {
|
||||
allTaskTargets: createStandardViewFlatMetadata({
|
||||
...args,
|
||||
objectName: 'taskTarget',
|
||||
context: {
|
||||
viewName: 'allTaskTargets',
|
||||
name: 'All Task Targets',
|
||||
type: ViewType.TABLE,
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
export const computeStandardTimelineActivityViews = (
|
||||
args: Omit<CreateStandardViewArgs<'timelineActivity'>, 'context'>,
|
||||
): Record<string, FlatView> => {
|
||||
return {
|
||||
allTimelineActivities: createStandardViewFlatMetadata({
|
||||
...args,
|
||||
objectName: 'timelineActivity',
|
||||
context: {
|
||||
viewName: 'allTimelineActivities',
|
||||
name: 'All Timeline Activities',
|
||||
type: ViewType.TABLE,
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
} from 'src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util';
|
||||
|
||||
export const computeStandardWorkspaceMemberViews = (
|
||||
args: Omit<CreateStandardViewArgs<'workspaceMember'>, 'context'>,
|
||||
): Record<string, FlatView> => {
|
||||
return {
|
||||
allWorkspaceMembers: createStandardViewFlatMetadata({
|
||||
...args,
|
||||
objectName: 'workspaceMember',
|
||||
context: {
|
||||
viewName: 'allWorkspaceMembers',
|
||||
name: 'All Workspace Members',
|
||||
type: ViewType.TABLE,
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -80,6 +80,46 @@ export const STANDARD_OBJECTS = {
|
||||
universalIdentifier: '14d0f5a9-6c81-4e3b-5f2a-8d9e0c1b2f36',
|
||||
},
|
||||
},
|
||||
views: {
|
||||
allAttachments: {
|
||||
universalIdentifier: '3f7f3363-7087-44cc-902d-5e8904262316',
|
||||
viewFields: {
|
||||
name: {
|
||||
universalIdentifier: 'be56712f-d7a6-4fbe-b92b-d750f0708a0a',
|
||||
},
|
||||
file: {
|
||||
universalIdentifier: '873cf114-5477-4b62-9023-7ea6ad69fbe5',
|
||||
},
|
||||
createdBy: {
|
||||
universalIdentifier: 'fa363372-0fdf-4bb3-bdf1-0ead354b9225',
|
||||
},
|
||||
createdAt: {
|
||||
universalIdentifier: '6c092c26-b1cb-488f-ae2e-5af4bec1162b',
|
||||
},
|
||||
targetPerson: {
|
||||
universalIdentifier: '73a4c3a7-c7f9-4ed6-a2b6-117d7efad0f3',
|
||||
},
|
||||
targetCompany: {
|
||||
universalIdentifier: 'b335ad04-059e-4c36-8666-f40431849044',
|
||||
},
|
||||
targetOpportunity: {
|
||||
universalIdentifier: '15f2d457-dc09-4c52-bf2a-47083d6bf017',
|
||||
},
|
||||
targetTask: {
|
||||
universalIdentifier: 'c2913c5e-6cc6-438d-9c2f-3212a9b2a82b',
|
||||
},
|
||||
targetNote: {
|
||||
universalIdentifier: 'fc8dba49-bcf2-41b8-a435-0c4a3bbf2af6',
|
||||
},
|
||||
targetDashboard: {
|
||||
universalIdentifier: 'bcc6d6e1-7c0b-4291-9270-66e42024d8dd',
|
||||
},
|
||||
targetWorkflow: {
|
||||
universalIdentifier: '11fcf58b-dbab-42dd-be67-689462111070',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
blocklist: {
|
||||
universalIdentifier: '20202020-0408-4f38-b8a8-4d5e3e26e24d',
|
||||
@@ -1152,6 +1192,28 @@ export const STANDARD_OBJECTS = {
|
||||
universalIdentifier: '2527a2b6-3558-4f0c-2a9b-56e7f58e9e57',
|
||||
},
|
||||
},
|
||||
views: {
|
||||
allNoteTargets: {
|
||||
universalIdentifier: 'd124d587-ef78-402b-9341-7673e6cea033',
|
||||
viewFields: {
|
||||
id: {
|
||||
universalIdentifier: 'f2d912fe-7c6f-4a9c-b808-b7b5a18d2818',
|
||||
},
|
||||
note: {
|
||||
universalIdentifier: '9d4ac173-d32b-4a44-9dbd-8a47ab844f98',
|
||||
},
|
||||
targetPerson: {
|
||||
universalIdentifier: 'b6f67de5-c5cf-4235-b740-a6a007c8eae3',
|
||||
},
|
||||
targetCompany: {
|
||||
universalIdentifier: 'a9c7f370-4b22-4f29-8e3f-678e91a59576',
|
||||
},
|
||||
targetOpportunity: {
|
||||
universalIdentifier: '3efeb162-cd03-458b-9c7b-47032d045204',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
opportunity: {
|
||||
universalIdentifier: '20202020-9549-49dd-b2b2-883999db8938',
|
||||
@@ -1716,6 +1778,28 @@ export const STANDARD_OBJECTS = {
|
||||
universalIdentifier: '5850b5c9-6881-4a3d-5b2c-89f0a81f2f80',
|
||||
},
|
||||
},
|
||||
views: {
|
||||
allTaskTargets: {
|
||||
universalIdentifier: '1dbf1d24-6cca-4f55-ae2f-e3d1b425a495',
|
||||
viewFields: {
|
||||
id: {
|
||||
universalIdentifier: 'a49287c9-8aa6-4fca-9ec5-08d643f7239f',
|
||||
},
|
||||
task: {
|
||||
universalIdentifier: '1f79839e-42f6-4a69-839a-369e21a7497d',
|
||||
},
|
||||
targetPerson: {
|
||||
universalIdentifier: 'cadc7a33-1527-4ef8-ac00-7ed0b54d1bae',
|
||||
},
|
||||
targetCompany: {
|
||||
universalIdentifier: 'e9fa1305-4ba2-41c5-9198-fdc622b69f90',
|
||||
},
|
||||
targetOpportunity: {
|
||||
universalIdentifier: '526f3354-34d6-4e7e-a870-5f99c28353c2',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
timelineActivity: {
|
||||
universalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5',
|
||||
@@ -1831,6 +1915,33 @@ export const STANDARD_OBJECTS = {
|
||||
linkedRecordCachedName: {
|
||||
universalIdentifier: '20202020-bf01-4b01-8b01-ba5cc01aa017',
|
||||
},
|
||||
targetPerson: {
|
||||
universalIdentifier: '37b38a8b-abd7-4f72-92d2-ad82bbef0296',
|
||||
},
|
||||
targetCompany: {
|
||||
universalIdentifier: '2015825f-0786-4b0d-88a7-dfce1b4b1c1a',
|
||||
},
|
||||
targetOpportunity: {
|
||||
universalIdentifier: 'f7b5ced9-eba6-4454-8849-7a92d27c11ca',
|
||||
},
|
||||
targetTask: {
|
||||
universalIdentifier: '3899138d-e6fa-414c-9432-214c9b797ebb',
|
||||
},
|
||||
targetNote: {
|
||||
universalIdentifier: 'ab74ed52-0195-4b65-987a-8367c07ee222',
|
||||
},
|
||||
targetWorkflow: {
|
||||
universalIdentifier: 'd2c3ddc3-afad-40b9-a2cb-d2765f2f5691',
|
||||
},
|
||||
targetWorkflowVersion: {
|
||||
universalIdentifier: '4a7e3213-afd5-4691-8bba-0a10e8697afb',
|
||||
},
|
||||
targetWorkflowRun: {
|
||||
universalIdentifier: '97910946-04f0-4634-804e-880bc0019225',
|
||||
},
|
||||
targetDashboard: {
|
||||
universalIdentifier: '538847e8-ab09-407c-a433-505f6d7be7a1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2213,6 +2324,12 @@ export const STANDARD_OBJECTS = {
|
||||
createdAt: {
|
||||
universalIdentifier: '20202020-ef01-4e01-8e01-a0bcaeabe1f8',
|
||||
},
|
||||
ownedOpportunities: {
|
||||
universalIdentifier: '8a0503f3-ba61-453e-86dc-6c79f7bc235b',
|
||||
},
|
||||
assignedTasks: {
|
||||
universalIdentifier: 'af16226e-6375-4676-8bd9-9d1a57076fc4',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user