Deprecate old relations completely (#12482)

# What

Fully deprecate old relations because we have one bug tied to it and it
make the codebase complex

# How I've made this PR:
1. remove metadata datasource (we only keep 'core') => this was causing
extra complexity in the refactor + flaky reset
2. merge dev and demo datasets => as I needed to update the tests which
is very painful, I don't want to do it twice
3. remove all code tied to RELATION_METADATA /
relation-metadata.resolver, or anything tied to the old relation system
4. Remove ONE_TO_ONE and MANY_TO_MANY that are not supported
5. fix impacts on the different areas : see functional testing below 

# Functional testing

## Functional testing from the front-end:
1. Database Reset 
2. Sign In 
3. Workspace sign-up 
5. Browsing table / kanban / show 
6. Assigning a record in a one to many / in a many to one 
7. Deleting a record involved in a relation  => broken but not tied to
this PR
8. "Add new" from relation picker  => broken but not tied to this PR
9. Creating a Task / Note, Updating a Task / Note relations, Deleting a
Task / Note (from table, show page, right drawer)  => broken but not
tied to this PR
10. creating a relation from settings (custom / standard x oneToMany /
manyToOne) 
11. updating a relation from settings should not be possible 
12. deleting a relation from settings (custom / standard x oneToMany /
manyToOne) 
13. Make sure timeline activity still work (relation were involved
there), espacially with Task / Note => to be double checked  => Cannot
convert undefined or null to object
14. Workspace deletion / User deletion  
15. CSV Import should keep working  
16. Permissions: I have tested without permissions V2 as it's still hard
to test v2 work and it's not in prod yet 
17. Workflows global test  

## From the API:
1. Review open-api documentation (REST)  
2. Make sure REST Api are still able to fetch relations ==> won't do, we
have a coupling Get/Update/Create there, this requires refactoring
3. Make sure REST Api is still able to update / remove relation => won't
do same

## Automated tests
1. lint + typescript 
2. front unit tests: 
3. server unit tests 2 
4. front stories: 
5. server integration: 
6. chromatic check : expected 0
7. e2e check : expected no more that current failures

## Remove // Todos
1. All are captured by functional tests above, nothing additional to do

## (Un)related regressions
1. Table loading state is not working anymore, we see the empty state
before table content
2. Filtering by Creator Tim Ap return empty results
3. Not possible to add Tasks / Notes / Files from show page

# Result

## New seeds that can be easily extended
<img width="1920" alt="image"
src="https://github.com/user-attachments/assets/d290d130-2a5f-44e6-b419-7e42a89eec4b"
/>

## -5k lines of code
## No more 'metadata' dataSource (we only have 'core)
## No more relationMetadata (I haven't drop the table yet it's not
referenced in the code anymore)
## We are ready to fix the 6 months lag between current API results and
our mocked tests
## No more bug on relation creation / deletion

---------

Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Charles Bochet
2025-06-10 16:45:27 +02:00
committed by GitHub
parent 264861e020
commit a68895189c
426 changed files with 48870 additions and 54125 deletions

View File

@ -12,14 +12,12 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { SeederService } from 'src/engine/seeder/seeder.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@ -29,7 +27,6 @@ describe('WorkspaceManagerService', () => {
let objectMetadataService: ObjectMetadataService;
let workspaceMigrationRepository: Repository<WorkspaceMigrationEntity>;
let dataSourceRepository: Repository<DataSourceEntity>;
let workspaceRelationMetadataRepository: Repository<RelationMetadataEntity>;
let workspaceFieldMetadataRepository: Repository<FieldMetadataEntity>;
let workspaceDataSourceService: WorkspaceDataSourceService;
let userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>;
@ -50,43 +47,37 @@ describe('WorkspaceManagerService', () => {
useValue: {},
},
{
provide: getRepositoryToken(FieldMetadataEntity, 'metadata'),
provide: getRepositoryToken(FieldMetadataEntity, 'core'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(RelationMetadataEntity, 'metadata'),
provide: getRepositoryToken(ObjectMetadataEntity, 'core'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(ObjectMetadataEntity, 'metadata'),
provide: getRepositoryToken(WorkspaceMigrationEntity, 'core'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(WorkspaceMigrationEntity, 'metadata'),
provide: getRepositoryToken(DataSourceEntity, 'core'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(DataSourceEntity, 'metadata'),
provide: getRepositoryToken(UserWorkspaceRoleEntity, 'core'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(UserWorkspaceRoleEntity, 'metadata'),
useValue: {
delete: jest.fn(),
},
},
{
provide: getRepositoryToken(RoleEntity, 'metadata'),
provide: getRepositoryToken(RoleEntity, 'core'),
useValue: {
delete: jest.fn(),
},
@ -117,10 +108,7 @@ describe('WorkspaceManagerService', () => {
provide: WorkspaceSyncMetadataService,
useValue: {},
},
{
provide: SeederService,
useValue: {},
},
{
provide: ObjectMetadataService,
useValue: {
@ -136,24 +124,21 @@ describe('WorkspaceManagerService', () => {
);
workspaceMigrationRepository = module.get<
Repository<WorkspaceMigrationEntity>
>(getRepositoryToken(WorkspaceMigrationEntity, 'metadata'));
>(getRepositoryToken(WorkspaceMigrationEntity, 'core'));
dataSourceRepository = module.get<Repository<DataSourceEntity>>(
getRepositoryToken(DataSourceEntity, 'metadata'),
getRepositoryToken(DataSourceEntity, 'core'),
);
workspaceRelationMetadataRepository = module.get<
Repository<RelationMetadataEntity>
>(getRepositoryToken(RelationMetadataEntity, 'metadata'));
workspaceFieldMetadataRepository = module.get<
Repository<FieldMetadataEntity>
>(getRepositoryToken(FieldMetadataEntity, 'metadata'));
>(getRepositoryToken(FieldMetadataEntity, 'core'));
workspaceDataSourceService = module.get<WorkspaceDataSourceService>(
WorkspaceDataSourceService,
);
userWorkspaceRoleRepository = module.get<
Repository<UserWorkspaceRoleEntity>
>(getRepositoryToken(UserWorkspaceRoleEntity, 'metadata'));
>(getRepositoryToken(UserWorkspaceRoleEntity, 'core'));
roleRepository = module.get<Repository<RoleEntity>>(
getRepositoryToken(RoleEntity, 'metadata'),
getRepositoryToken(RoleEntity, 'core'),
);
});
@ -165,9 +150,6 @@ describe('WorkspaceManagerService', () => {
it('should delete all the workspace metadata tables and workspace schema', async () => {
await service.delete('workspace-id');
expect(objectMetadataService.deleteObjectsMetadata).toHaveBeenCalled();
expect(workspaceRelationMetadataRepository.delete).toHaveBeenCalledWith({
workspaceId: 'workspace-id',
});
expect(workspaceFieldMetadataRepository.delete).toHaveBeenCalledWith({
workspaceId: 'workspace-id',
});

View File

@ -1,31 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { COMPANIES_DEMO } from 'src/engine/workspace-manager/demo-objects-prefill-data/companies-demo.json';
export const seedCompanyWithDemoData = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.company`, [
'name',
'domainNamePrimaryLinkUrl',
'addressAddressCity',
'employees',
'linkedinLinkPrimaryLinkUrl',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'position'
])
.orIgnore()
.values(
COMPANIES_DEMO.map((company, index) => ({ ...company, position: index })),
)
.returning('*')
.execute();
};

View File

@ -1,78 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { DEMO_SEED_WORKSPACE_MEMBER_IDS } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data';
import { v4 } from 'uuid';
const tableName = 'opportunity';
const getRandomStage = () => {
const stages = ['NEW', 'SCREENING', 'MEETING', 'PROPOSAL', 'CUSTOMER'];
return stages[Math.floor(Math.random() * stages.length)];
};
const generateRandomAmountMicros = () => {
const firstDigit = Math.floor(Math.random() * 9) + 1;
return firstDigit * 10000000000;
};
// @ts-expect-error legacy noImplicitAny
const generateOpportunities = (companies) => {
// @ts-expect-error legacy noImplicitAny
return companies.map((company) => ({
id: v4(),
name: company.name,
amountAmountMicros: generateRandomAmountMicros(),
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: getRandomStage(),
pointOfContactId: company.personId,
companyId: company.id,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEMO_SEED_WORKSPACE_MEMBER_IDS.NOAH,
createdByName: 'Noah A',
}));
};
export const seedOpportunityWithDemoData = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
const companiesWithPeople = await entityManager?.query(
`SELECT company.*, person.id AS "personId"
FROM ${schemaName}.company
LEFT JOIN ${schemaName}.person ON company.id = "person"."companyId"
LIMIT 50`,
);
const opportunities = generateOpportunities(companiesWithPeople);
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'name',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'stage',
'pointOfContactId',
'companyId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'position',
])
.orIgnore()
.values(
// @ts-expect-error legacy noImplicitAny
opportunities.map((opportunity, index) => ({
...opportunity,
position: index,
})),
)
.execute();
};

View File

@ -1,52 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { peopleDemo } from 'src/engine/workspace-manager/demo-objects-prefill-data/people-demo.json';
export const seedPersonWithDemoData = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
const companies = await entityManager?.query(
`SELECT * FROM ${schemaName}.company`,
);
// Iterate through the array and add a UUID for each person
const people = peopleDemo.map((person, index) => ({
nameFirstName: person.firstName,
nameLastName: person.lastName,
emailsPrimaryEmail: person.email,
linkedinLinkPrimaryLinkUrl: person.linkedinUrl,
jobTitle: person.jobTitle,
city: person.city,
avatarUrl: person.avatarUrl,
companyId: companies[Math.floor(index / 2)].id,
createdBySource: person.createdBySource,
createdByWorkspaceMemberId: person.createdByWorkspaceMemberId,
createdByName: person.createdByName,
position: index
}));
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.person`, [
'nameFirstName',
'nameLastName',
'emailsPrimaryEmail',
'linkedinLinkPrimaryLinkUrl',
'jobTitle',
'city',
'avatarUrl',
'companyId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'position',
])
.orIgnore()
.values(people)
.returning('*')
.execute();
};

View File

@ -1,61 +0,0 @@
import { DEMO_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/demo/users';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
export const DEMO_SEED_WORKSPACE_MEMBER_IDS = {
NOAH: '20202020-0687-4c41-b707-ed1bfca972a2',
HUGO: '20202020-77d5-4cb6-b60a-f4a835a85d62',
TIM: '20202020-1553-45c6-a028-5a9064cce07e',
};
export const seedWorkspaceMemberWithDemoData = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.workspaceMember`, [
'id',
'nameFirstName',
'nameLastName',
'locale',
'colorScheme',
'userEmail',
'userId',
])
.orIgnore()
.values([
{
id: DEMO_SEED_WORKSPACE_MEMBER_IDS.NOAH,
nameFirstName: 'Noah',
nameLastName: 'A',
locale: SOURCE_LOCALE,
colorScheme: 'Light',
userEmail: 'noah@demo.dev',
userId: DEMO_SEED_USER_IDS.NOAH,
},
{
id: DEMO_SEED_WORKSPACE_MEMBER_IDS.HUGO,
nameFirstName: 'Hugo',
nameLastName: 'I',
locale: SOURCE_LOCALE,
colorScheme: 'Light',
userEmail: 'hugo@demo.dev',
userId: DEMO_SEED_USER_IDS.HUGO,
},
{
id: DEMO_SEED_WORKSPACE_MEMBER_IDS.TIM,
nameFirstName: 'Tim',
nameLastName: 'Apple',
locale: SOURCE_LOCALE,
colorScheme: 'Light',
userEmail: 'tim@apple.dev',
userId: DEMO_SEED_USER_IDS.TIM,
},
])
.execute();
};

View File

@ -1,55 +0,0 @@
import { DataSource } from 'typeorm';
import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
import { seedCompanyWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data';
import { seedOpportunityWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data';
import { seedPersonWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-person-with-demo-data';
import { seedWorkspaceMemberWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data';
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
export const seedWorkspaceWithDemoData = async (
workspaceDataSource: DataSource,
schemaName: string,
objectMetadata: ObjectMetadataEntity[],
) => {
const objectMetadataMap = objectMetadata.reduce((acc, object) => {
// @ts-expect-error legacy noImplicitAny
acc[object.standardId ?? ''] = {
id: object.id,
fields: object.fields.reduce((acc, field) => {
// @ts-expect-error legacy noImplicitAny
acc[field.standardId ?? ''] = field.id;
return acc;
}, {}),
};
return acc;
}, {});
await workspaceDataSource.transaction(
async (entityManager: WorkspaceEntityManager) => {
await seedCompanyWithDemoData(entityManager, schemaName);
await seedPersonWithDemoData(entityManager, schemaName);
await seedOpportunityWithDemoData(entityManager, schemaName);
const viewDefinitionsWithId = await seedViewWithDemoData(
entityManager,
schemaName,
objectMetadataMap,
);
await seedWorkspaceFavorites(
viewDefinitionsWithId
.filter((view) => view.key === 'INDEX' && shouldSeedWorkspaceFavorite(view.objectMetadataId, objectMetadataMap))
.map((view) => view.id),
entityManager,
schemaName,
);
await seedWorkspaceMemberWithDemoData(entityManager, schemaName);
},
);
};

View File

@ -0,0 +1,33 @@
import { DataSource } from 'typeorm';
const tableName = 'billingSubscription';
export const seedBillingSubscriptions = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'workspaceId',
'stripeCustomerId',
'stripeSubscriptionId',
'status',
'metadata',
])
.orIgnore()
.values([
{
workspaceId,
stripeCustomerId: 'cus_default0',
stripeSubscriptionId: 'sub_default0',
status: 'active',
metadata: {
workspaceId,
},
},
])
.execute();
};

View File

@ -0,0 +1,78 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { Repository } from 'typeorm';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
@Injectable()
export class DevSeederPermissionsService {
private readonly logger = new Logger(DevSeederPermissionsService.name);
constructor(
private readonly roleService: RoleService,
private readonly userRoleService: UserRoleService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
) {}
public async initPermissions(workspaceId: string) {
const adminRole = await this.roleService.createAdminRole({
workspaceId,
});
let adminUserWorkspaceId: string | undefined;
let memberUserWorkspaceId: string | undefined;
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM;
memberUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.JONY;
// Create guest role only in this workspace
const guestRole = await this.roleService.createGuestRole({
workspaceId,
});
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.PHIL,
roleId: guestRole.id,
});
} else if (workspaceId === SEED_ACME_WORKSPACE_ID) {
adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM_ACME;
}
if (adminUserWorkspaceId) {
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: adminUserWorkspaceId,
roleId: adminRole.id,
});
}
const memberRole = await this.roleService.createMemberRole({
workspaceId,
});
await this.workspaceRepository.update(workspaceId, {
defaultRoleId: memberRole.id,
activationStatus: WorkspaceActivationStatus.ACTIVE,
});
if (memberUserWorkspaceId) {
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: memberUserWorkspaceId,
roleId: memberRole.id,
});
}
}
}

View File

@ -0,0 +1,42 @@
import { DataSource } from 'typeorm';
import { seedBillingSubscriptions } from 'src/engine/workspace-manager/dev-seeder/core/billing/utils/seed-billing-subscriptions.util';
import { seedFeatureFlags } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util';
import { seedUserWorkspaces } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import { seedUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
import { seedWorkspaces } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
type SeedCoreSchemaArgs = {
dataSource: DataSource;
workspaceId: string;
appVersion: string | undefined;
seedBilling?: boolean;
seedFeatureFlags?: boolean;
};
export const seedCoreSchema = async ({
appVersion,
dataSource,
workspaceId,
seedBilling = true,
seedFeatureFlags: shouldSeedFeatureFlags = true,
}: SeedCoreSchemaArgs) => {
const schemaName = 'core';
await seedWorkspaces({
dataSource,
schemaName,
workspaceId,
appVersion,
});
await seedUsers(dataSource, schemaName);
await seedUserWorkspaces(dataSource, schemaName, workspaceId);
if (shouldSeedFeatureFlags) {
await seedFeatureFlags(dataSource, schemaName, workspaceId);
}
if (seedBilling) {
await seedBillingSubscriptions(dataSource, schemaName, workspaceId);
}
};

View File

@ -0,0 +1,63 @@
import { DataSource } from 'typeorm';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
const tableName = 'featureFlag';
export const seedFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value'])
.orIgnore()
.values([
{
key: FeatureFlagKey.IS_AIRTABLE_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_POSTGRESQL_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_STRIPE_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_UNIQUE_INDEXES_ENABLED,
workspaceId: workspaceId,
value: false,
},
{
key: FeatureFlagKey.IS_AI_ENABLED,
workspaceId: workspaceId,
value: true,
},
])
.execute();
};
export const deleteFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, { workspaceId })
.execute();
};

View File

@ -0,0 +1,90 @@
import { DataSource } from 'typeorm';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
const tableName = 'userWorkspace';
export const USER_WORKSPACE_DATA_SEED_IDS = {
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b035',
JONY: '20202020-3957-4908-9c36-2929a23f8353',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b1',
TIM_ACME: '20202020-e10a-4c27-a90b-b08c57b02d44',
JONY_ACME: '20202020-e10a-4c27-a90b-b08c57b02d45',
PHIL_ACME: '20202020-e10a-4c27-a90b-b08c57b02d46',
};
export const seedUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
let userWorkspaces: Pick<UserWorkspace, 'id' | 'userId' | 'workspaceId'>[] =
[];
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
userWorkspaces = [
{
id: USER_WORKSPACE_DATA_SEED_IDS.TIM,
userId: USER_DATA_SEED_IDS.TIM,
workspaceId,
},
{
id: USER_WORKSPACE_DATA_SEED_IDS.JONY,
userId: USER_DATA_SEED_IDS.JONY,
workspaceId,
},
{
id: USER_WORKSPACE_DATA_SEED_IDS.PHIL,
userId: USER_DATA_SEED_IDS.PHIL,
workspaceId,
},
];
}
if (workspaceId === SEED_ACME_WORKSPACE_ID) {
userWorkspaces = [
{
id: USER_WORKSPACE_DATA_SEED_IDS.TIM_ACME,
userId: USER_DATA_SEED_IDS.TIM,
workspaceId,
},
{
id: USER_WORKSPACE_DATA_SEED_IDS.JONY_ACME,
userId: USER_DATA_SEED_IDS.JONY,
workspaceId,
},
{
id: USER_WORKSPACE_DATA_SEED_IDS.PHIL_ACME,
userId: USER_DATA_SEED_IDS.PHIL,
workspaceId,
},
];
}
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
.orIgnore()
.values(userWorkspaces)
.execute();
};
export const deleteUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, {
workspaceId,
})
.execute();
};

View File

@ -0,0 +1,62 @@
import { DataSource } from 'typeorm';
const tableName = 'user';
export const USER_DATA_SEED_IDS = {
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034',
JONY: '20202020-3957-4908-9c36-2929a23f8357',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b8',
};
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'firstName',
'lastName',
'email',
'passwordHash',
'canImpersonate',
'canAccessFullAdminPanel',
'isEmailVerified',
])
.orIgnore()
.values([
{
id: USER_DATA_SEED_IDS.TIM,
firstName: 'Tim',
lastName: 'Apple',
email: 'tim@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
{
id: USER_DATA_SEED_IDS.JONY,
firstName: 'Jony',
lastName: 'Ive',
email: 'jony.ive@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
{
id: USER_DATA_SEED_IDS.PHIL,
firstName: 'Phil',
lastName: 'Schiler',
email: 'phil.schiler@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
])
.execute();
};

View File

@ -0,0 +1,83 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { DataSource } from 'typeorm';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
const tableName = 'workspace';
export const SEED_APPLE_WORKSPACE_ID = '20202020-1c25-4d02-bf25-6aeccf7ea419';
export const SEED_ACME_WORKSPACE_ID = '3b8e6458-5fc1-4e63-8563-008ccddaa6db';
export type SeedWorkspaceArgs = {
dataSource: DataSource;
schemaName: string;
workspaceId: string;
appVersion: string | undefined;
};
const workspaceSeederFields = [
'id',
'displayName',
'subdomain',
'inviteHash',
'logo',
'activationStatus',
'version',
] as const satisfies (keyof Workspace)[];
type WorkspaceSeederFields = Pick<
Workspace,
(typeof workspaceSeederFields)[number]
>;
export const seedWorkspaces = async ({
schemaName,
dataSource,
workspaceId,
appVersion,
}: SeedWorkspaceArgs) => {
const version = extractVersionMajorMinorPatch(appVersion);
const workspaces: Record<string, WorkspaceSeederFields> = {
[SEED_APPLE_WORKSPACE_ID]: {
id: SEED_APPLE_WORKSPACE_ID,
displayName: 'Apple',
subdomain: 'apple',
inviteHash: 'apple.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/apple-logo.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
version: version,
},
[SEED_ACME_WORKSPACE_ID]: {
id: SEED_ACME_WORKSPACE_ID,
displayName: 'Acme',
subdomain: 'acme',
inviteHash: 'acme.dev-invite-hash',
logo: 'https://logos-world.net/wp-content/uploads/2022/05/Acme-Logo-700x394.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
version: version,
},
};
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, workspaceSeederFields)
.orIgnore()
.values(workspaces[workspaceId])
.execute();
};
export const deleteWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`${tableName}."id" = :id`, { id: workspaceId })
.execute();
};

View File

@ -0,0 +1,25 @@
type ApiKeyDataSeed = {
id: string;
name: string;
expiresAt: Date;
};
export const API_KEY_DATA_SEED_COLUMNS: (keyof ApiKeyDataSeed)[] = [
'id',
'name',
'expiresAt',
];
export const API_KEY_DATA_SEED_IDS = {
ID_1: '20202020-f401-4d8a-a731-64d007c27bad',
};
export const API_KEY_DATA_SEEDS: ApiKeyDataSeed[] = [
{
id: API_KEY_DATA_SEED_IDS.ID_1,
name: 'My api key',
expiresAt: new Date(
new Date().getTime() + 1000 * 60 * 60 * 24 * 365 * 100, // In 100 years
),
},
];

View File

@ -0,0 +1,36 @@
import { CONNECTED_ACCOUNT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/connected-account-data-seeds.constant';
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
type CalendarChannelDataSeed = {
id: string;
connectedAccountId: string;
handle: string;
visibility: CalendarChannelVisibility;
isContactAutoCreationEnabled: boolean;
isSyncEnabled: boolean;
};
export const CALENDAR_CHANNEL_DATA_SEED_COLUMNS: (keyof CalendarChannelDataSeed)[] =
[
'id',
'connectedAccountId',
'handle',
'visibility',
'isContactAutoCreationEnabled',
'isSyncEnabled',
];
export const CALENDAR_CHANNEL_DATA_SEED_IDS = {
TIM: '20202020-a40f-4faf-bb9f-c6f9945b8203',
};
export const CALENDAR_CHANNEL_DATA_SEEDS: CalendarChannelDataSeed[] = [
{
id: CALENDAR_CHANNEL_DATA_SEED_IDS.TIM,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
isContactAutoCreationEnabled: true,
isSyncEnabled: true,
},
];

View File

@ -0,0 +1,34 @@
import { CALENDAR_CHANNEL_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-channel-data-seeds.constant';
import { CALENDAR_EVENT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
type CalendarChannelEventAssociationDataSeed = {
id: string;
calendarChannelId: string;
calendarEventId: string;
eventExternalId: string;
recurringEventExternalId: string;
};
export const CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEED_COLUMNS: (keyof CalendarChannelEventAssociationDataSeed)[] =
[
'id',
'calendarChannelId',
'calendarEventId',
'eventExternalId',
'recurringEventExternalId',
];
export const CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEED_IDS = {
ID_1: '20202020-0687-4c41-b707-ed1bfca972a2',
};
export const CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEEDS: CalendarChannelEventAssociationDataSeed[] =
[
{
id: CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEED_IDS.ID_1,
calendarChannelId: CALENDAR_CHANNEL_DATA_SEED_IDS.TIM,
calendarEventId: CALENDAR_EVENT_DATA_SEED_IDS.ID_1,
eventExternalId: 'exampleExternalId',
recurringEventExternalId: 'exampleRecurringExternalId',
},
];

View File

@ -0,0 +1,57 @@
type CalendarEventDataSeed = {
id: string;
title: string;
isCanceled: boolean;
isFullDay: boolean;
startsAt: string;
endsAt: string;
externalCreatedAt: string;
externalUpdatedAt: string;
description: string;
location: string;
iCalUID: string;
conferenceSolution: string;
conferenceLinkPrimaryLinkLabel: string;
conferenceLinkPrimaryLinkUrl: string;
};
export const CALENDAR_EVENT_DATA_SEED_COLUMNS: (keyof CalendarEventDataSeed)[] =
[
'id',
'title',
'isCanceled',
'isFullDay',
'startsAt',
'endsAt',
'externalCreatedAt',
'externalUpdatedAt',
'description',
'location',
'iCalUID',
'conferenceSolution',
'conferenceLinkPrimaryLinkLabel',
'conferenceLinkPrimaryLinkUrl',
];
export const CALENDAR_EVENT_DATA_SEED_IDS = {
ID_1: '20202020-1c0e-494c-a1b6-85b1c6fefaa5',
};
export const CALENDAR_EVENT_DATA_SEEDS: CalendarEventDataSeed[] = [
{
id: CALENDAR_EVENT_DATA_SEED_IDS.ID_1,
title: 'Meeting with Christoph',
isCanceled: false,
isFullDay: false,
startsAt: new Date(new Date().setHours(10, 0)).toISOString(),
endsAt: new Date(new Date().setHours(11, 0)).toISOString(),
externalCreatedAt: new Date().toISOString(),
externalUpdatedAt: new Date().toISOString(),
description: 'Discuss project progress',
location: 'Seattle',
iCalUID: 'event1@calendar.com',
conferenceSolution: 'Zoom',
conferenceLinkPrimaryLinkLabel: 'https://zoom.us/j/1234567890',
conferenceLinkPrimaryLinkUrl: 'https://zoom.us/j/1234567890',
},
];

View File

@ -0,0 +1,55 @@
import { CALENDAR_EVENT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
type CalendarEventParticipantDataSeed = {
id: string;
calendarEventId: string;
handle: string;
displayName: string;
isOrganizer: boolean;
responseStatus: CalendarEventParticipantResponseStatus;
personId: string | null;
workspaceMemberId: string | null;
};
export const CALENDAR_EVENT_PARTICIPANT_DATA_SEED_COLUMNS = [
'id',
'calendarEventId',
'handle',
'displayName',
'isOrganizer',
'responseStatus',
'personId',
'workspaceMemberId',
];
export const CALENDAR_EVENT_PARTICIPANT_DATA_SEED_IDS = {
ONE: '20202020-fb8f-4f0d-a36e-950e185401d4',
TWO: '20202020-0722-40d7-9e55-cb5d00cfb654',
};
export const CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS: CalendarEventParticipantDataSeed[] =
[
{
id: CALENDAR_EVENT_PARTICIPANT_DATA_SEED_IDS.ONE,
calendarEventId: CALENDAR_EVENT_DATA_SEED_IDS.ID_1,
handle: 'christoph.calisto@linkedin.com',
displayName: 'Christoph Calisto',
isOrganizer: true,
responseStatus: CalendarEventParticipantResponseStatus.ACCEPTED,
personId: PERSON_DATA_SEED_IDS.ID_1,
workspaceMemberId: null,
},
{
id: CALENDAR_EVENT_PARTICIPANT_DATA_SEED_IDS.TWO,
calendarEventId: CALENDAR_EVENT_DATA_SEED_IDS.ID_1,
handle: 'tim@apple.com',
displayName: 'Tim Apple',
isOrganizer: false,
responseStatus: CalendarEventParticipantResponseStatus.ACCEPTED,
personId: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
},
];

View File

@ -0,0 +1,73 @@
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
type ConnectedAccountDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
lastSyncHistoryId: string;
accountOwnerId: string;
refreshToken: string;
accessToken: string;
provider: string;
handle: string;
};
export const CONNECTED_ACCOUNT_DATA_SEED_COLUMNS: (keyof ConnectedAccountDataSeed)[] =
[
'id',
'createdAt',
'updatedAt',
'deletedAt',
'lastSyncHistoryId',
'accountOwnerId',
'refreshToken',
'accessToken',
'provider',
'handle',
];
export const CONNECTED_ACCOUNT_DATA_SEED_IDS = {
TIM: '20202020-9ac0-4390-9a1a-ab4d2c4e1bb7',
JONY: '20202020-0cc8-4d60-a3a4-803245698908',
PHIL: '20202020-cafc-4323-908d-e5b42ad69fdf',
};
export const CONNECTED_ACCOUNT_DATA_SEEDS: ConnectedAccountDataSeed[] = [
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'tim@apple.dev',
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: WORKSPACE_MEMBER_DATA_SEED_IDS.JONY,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'jony.ive@apple.dev',
},
{
id: CONNECTED_ACCOUNT_DATA_SEED_IDS.PHIL,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: WORKSPACE_MEMBER_DATA_SEED_IDS.PHIL,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'phil.schiler@apple.dev',
},
];

View File

@ -0,0 +1,82 @@
import { CONNECTED_ACCOUNT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/connected-account-data-seeds.constant';
import {
MessageChannelSyncStage,
MessageChannelVisibility,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
type MessageChannelDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
isContactAutoCreationEnabled: boolean;
type: string;
connectedAccountId: string;
handle: string;
isSyncEnabled: boolean;
visibility: MessageChannelVisibility;
syncStage: MessageChannelSyncStage;
};
export const MESSAGE_CHANNEL_DATA_SEED_COLUMNS: (keyof MessageChannelDataSeed)[] =
[
'id',
'createdAt',
'updatedAt',
'deletedAt',
'isContactAutoCreationEnabled',
'type',
'connectedAccountId',
'handle',
'isSyncEnabled',
'visibility',
'syncStage',
];
export const MESSAGE_CHANNEL_DATA_SEED_IDS = {
TIM: '20202020-9b80-4c2c-a597-383db48de1d6',
JONY: '20202020-5ffe-4b32-814a-983d5e4911cd',
PHIL: '20202020-e2f1-49b5-85d2-5d3a3386990c',
};
export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.JONY,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
handle: 'jony.ive@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
{
id: MESSAGE_CHANNEL_DATA_SEED_IDS.PHIL,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.PHIL,
handle: 'phil.schiler@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
];

View File

@ -0,0 +1,71 @@
import { MESSAGE_CHANNEL_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant';
import { MESSAGE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
type MessageChannelMessageAssociationDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
messageThreadExternalId: string | null;
messageExternalId: string | null;
messageId: string;
messageChannelId: string;
direction: MessageDirection;
};
export const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_COLUMNS: (keyof MessageChannelMessageAssociationDataSeed)[] =
[
'id',
'createdAt',
'updatedAt',
'deletedAt',
'messageThreadExternalId',
'messageExternalId',
'messageId',
'messageChannelId',
'direction',
];
export const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_IDS = {
ID_1: '20202020-cc69-44ef-a82c-600c0dbf39ba',
ID_2: '20202020-d80e-4a13-b10b-72ba09082668',
ID_3: '20202020-e6ec-4c8a-b431-0901eaf395a9',
};
export const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEEDS: MessageChannelMessageAssociationDataSeed[] =
[
{
id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_IDS.ID_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: MESSAGE_DATA_SEED_IDS.ID_1,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
direction: MessageDirection.OUTGOING,
},
{
id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_IDS.ID_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: MESSAGE_DATA_SEED_IDS.ID_2,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
direction: MessageDirection.OUTGOING,
},
{
id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_IDS.ID_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: MESSAGE_DATA_SEED_IDS.ID_3,
messageChannelId: MESSAGE_CHANNEL_DATA_SEED_IDS.TIM,
direction: MessageDirection.INCOMING,
},
];

View File

@ -0,0 +1,67 @@
import { MESSAGE_THREAD_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-thread-data-seeds.constant';
type MessageDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
receivedAt: Date;
text: string;
subject: string;
messageThreadId: string;
headerMessageId: string;
};
export const MESSAGE_DATA_SEED_COLUMNS: (keyof MessageDataSeed)[] = [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'receivedAt',
'text',
'subject',
'messageThreadId',
'headerMessageId',
];
export const MESSAGE_DATA_SEED_IDS = {
ID_1: '20202020-2b8a-405d-8f42-e820ca921421',
ID_2: '20202020-04c8-4f24-93f2-764948e95014',
ID_3: '20202020-ac6b-4f86-87a2-5f5f9d1b6481',
};
export const MESSAGE_DATA_SEEDS: MessageDataSeed[] = [
{
id: MESSAGE_DATA_SEED_IDS.ID_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Hello, \n I hope this email finds you well. I am writing to request a meeting. I believe it would be beneficial for both parties to collaborate and explore potential opportunities. Would you be available for a meeting sometime next week? Please let me know your availability, and I will arrange a suitable time. \n Looking forward to your response.\n Best regards',
subject: 'Meeting Request',
messageThreadId: MESSAGE_THREAD_DATA_SEED_IDS.ID_1,
headerMessageId: '99ef24a8-2b8a-405d-8f42-e820ca921421',
},
{
id: MESSAGE_DATA_SEED_IDS.ID_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Good Morning,\n I am writing to inquire about information. Could you please provide me with details regarding this topic? \n Your assistance in this matter would be greatly appreciated. Thank you in advance for your prompt response. \n Best regards,Tim',
subject: 'Inquiry Regarding Topic',
messageThreadId: MESSAGE_THREAD_DATA_SEED_IDS.ID_2,
headerMessageId: '8f804a9a-04c8-4f24-93f2-764948e95014',
},
{
id: MESSAGE_DATA_SEED_IDS.ID_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Good Evening,\nI wanted to extend my sincere gratitude for taking the time to meet with me earlier today. It was a pleasure discussing with you, and I am excited about the potential opportunities for collaboration. \n Please feel free to reach out if you have any further questions or require additional information. I look forward to our continued communication. Best regards.',
subject: 'Thank You for the Meeting',
messageThreadId: MESSAGE_THREAD_DATA_SEED_IDS.ID_1,
headerMessageId: '3939d68a-ac6b-4f86-87a2-5f5f9d1b6481',
},
];

View File

@ -0,0 +1,114 @@
import { MESSAGE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
type MessageParticipantDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
workspaceMemberId: string;
personId: string;
displayName: string;
handle: string;
role: string;
messageId: string;
};
export const MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS: (keyof MessageParticipantDataSeed)[] =
[
'id',
'createdAt',
'updatedAt',
'deletedAt',
'workspaceMemberId',
'personId',
'displayName',
'handle',
'role',
'messageId',
];
export const MESSAGE_PARTICIPANT_DATA_SEED_IDS = {
ID_1: '20202020-0f2a-49d8-8aa2-ec8786153a0b',
ID_2: '20202020-4e83-41ec-93e2-fd70ff09f68c',
ID_3: '20202020-e716-4dd5-ac61-3315bc559e2d',
ID_4: '20202020-fc7d-4ad8-9aea-b78bcbf79cdd',
ID_5: '20202020-564c-4a3c-abbf-e942e8c3f9c9',
ID_6: '20202020-7e4a-489a-ba6b-1ae6b7d721ac',
};
export const MESSAGE_PARTICIPANT_DATA_SEEDS: MessageParticipantDataSeed[] = [
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_1,
displayName: 'Christoph',
handle: 'outgoing',
role: 'from',
messageId: MESSAGE_DATA_SEED_IDS.ID_1,
},
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_2,
displayName: 'Sylvie',
handle: 'incoming',
role: 'to',
messageId: MESSAGE_DATA_SEED_IDS.ID_1,
},
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_3,
displayName: 'Christopher',
handle: 'incoming',
role: 'to',
messageId: MESSAGE_DATA_SEED_IDS.ID_1,
},
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_4,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_1,
displayName: 'Christoph',
handle: 'outgoing',
role: 'from',
messageId: MESSAGE_DATA_SEED_IDS.ID_2,
},
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_5,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_2,
displayName: 'Sylvie',
handle: 'incoming',
role: 'to',
messageId: MESSAGE_DATA_SEED_IDS.ID_2,
},
{
id: MESSAGE_PARTICIPANT_DATA_SEED_IDS.ID_6,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
personId: PERSON_DATA_SEED_IDS.ID_3,
displayName: 'Christopher',
handle: 'incoming',
role: 'to',
messageId: MESSAGE_DATA_SEED_IDS.ID_2,
},
];

View File

@ -0,0 +1,44 @@
type MessageThreadDataSeed = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
};
export const MESSAGE_THREAD_DATA_SEED_COLUMNS: (keyof MessageThreadDataSeed)[] =
['id', 'createdAt', 'updatedAt', 'deletedAt'];
export const MESSAGE_THREAD_DATA_SEED_IDS = {
ID_1: '20202020-8bfa-453b-b99b-bc435a7d4da8',
ID_2: '20202020-634a-4fde-aa7c-28a0eaf203ca',
ID_3: '20202020-1b56-4f10-a2fa-2ccaddf81f6c',
ID_4: '20202020-d51c-485a-b1b6-ed7c63e05d72',
ID_5: '20202020-3f74-492d-a101-2a70f50a1645',
};
export const MESSAGE_THREAD_DATA_SEEDS: MessageThreadDataSeed[] = [
{
id: MESSAGE_THREAD_DATA_SEED_IDS.ID_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: MESSAGE_THREAD_DATA_SEED_IDS.ID_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: MESSAGE_THREAD_DATA_SEED_IDS.ID_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: MESSAGE_THREAD_DATA_SEED_IDS.ID_4,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
];

View File

@ -0,0 +1,99 @@
import { COMPANY_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/company-data-seeds.constant';
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
type OpportunityDataSeed = {
id: string;
name: string;
amountAmountMicros: number;
amountCurrencyCode: string;
closeDate: Date;
stage: string;
position: number;
pointOfContactId: string;
companyId: string;
createdBySource: string;
createdByWorkspaceMemberId: string;
createdByName: string;
};
export const OPPORTUNITY_DATA_SEED_COLUMNS: (keyof OpportunityDataSeed)[] = [
'id',
'name',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'stage',
'position',
'pointOfContactId',
'companyId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
];
export const OPPORTUNITY_DATA_SEED_IDS = {
ID_1: '20202020-be10-422b-a663-16bd3c2228e1',
ID_2: '20202020-0543-4cc2-9f96-95cc699960f2',
ID_3: '20202020-2f89-406f-90ea-180f433b2445',
ID_4: '20202020-35b1-4045-9cde-42f715148954',
};
export const OPPORTUNITY_DATA_SEEDS: OpportunityDataSeed[] = [
{
id: OPPORTUNITY_DATA_SEED_IDS.ID_1,
name: 'Opportunity 1',
amountAmountMicros: 100000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'NEW',
position: 1,
pointOfContactId: PERSON_DATA_SEED_IDS.ID_1,
companyId: COMPANY_DATA_SEED_IDS.ID_1,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: OPPORTUNITY_DATA_SEED_IDS.ID_2,
name: 'Opportunity 2',
amountAmountMicros: 2000000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'MEETING',
position: 2,
pointOfContactId: PERSON_DATA_SEED_IDS.ID_2,
companyId: COMPANY_DATA_SEED_IDS.ID_2,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: OPPORTUNITY_DATA_SEED_IDS.ID_3,
name: 'Opportunity 3',
amountAmountMicros: 300000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'PROPOSAL',
position: 3,
pointOfContactId: PERSON_DATA_SEED_IDS.ID_3,
companyId: COMPANY_DATA_SEED_IDS.ID_3,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: OPPORTUNITY_DATA_SEED_IDS.ID_4,
name: 'Opportunity 4',
amountAmountMicros: 4000000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'PROPOSAL',
position: 4,
pointOfContactId: PERSON_DATA_SEED_IDS.ID_4,
companyId: COMPANY_DATA_SEED_IDS.ID_4,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdByName: '',
},
];

View File

@ -0,0 +1,204 @@
type PetDataSeed = {
id: string;
name: string;
species: string;
traits: string[];
comments: string;
age: number;
locationAddressStreet1: string;
locationAddressStreet2: string;
locationAddressCity: string;
locationAddressCountry: string;
locationAddressPostcode: string;
locationAddressState: string;
vetPhonePrimaryPhoneCallingCode: string;
vetPhonePrimaryPhoneCountryCode: string;
vetPhonePrimaryPhoneNumber: string;
vetEmailPrimaryEmail: string;
vetEmailAdditionalEmails: string;
birthday: string;
isGoodWithKids: boolean;
picturesPrimaryLinkUrl: string;
picturesPrimaryLinkLabel: string;
picturesSecondaryLinks: string;
averageCostOfKibblePerMonthAmountMicros: number;
averageCostOfKibblePerMonthCurrencyCode: string;
makesOwnerThinkOfFirstName: string;
makesOwnerThinkOfLastName: string;
soundSwag: string;
bio: string;
interestingFacts: string[];
extraData: string;
};
export const PET_DATA_SEED_COLUMNS: (keyof PetDataSeed)[] = [
'id',
'name',
'species',
'traits',
'comments',
'age',
'locationAddressStreet1',
'locationAddressStreet2',
'locationAddressCity',
'locationAddressCountry',
'locationAddressPostcode',
'locationAddressState',
'vetPhonePrimaryPhoneCallingCode',
'vetPhonePrimaryPhoneCountryCode',
'vetPhonePrimaryPhoneNumber',
'vetEmailPrimaryEmail',
'vetEmailAdditionalEmails',
'birthday',
'isGoodWithKids',
'picturesPrimaryLinkUrl',
'picturesPrimaryLinkLabel',
'picturesSecondaryLinks',
'averageCostOfKibblePerMonthAmountMicros',
'averageCostOfKibblePerMonthCurrencyCode',
'makesOwnerThinkOfFirstName',
'makesOwnerThinkOfLastName',
'soundSwag',
'bio',
'interestingFacts',
'extraData',
];
export const PET_DATA_SEED_IDS = {
ID_1: '20202020-0f2a-49d8-8aa2-ec8786153a0b',
};
export const PET_DATA_SEEDS: PetDataSeed[] = [
{
id: PET_DATA_SEED_IDS.ID_1,
name: 'Toby',
species: 'DOG',
traits: ['CURIOUS', 'FRIENDLY'],
comments: 'Needs to have people around.',
age: 3,
locationAddressStreet1: '513 Batz Fork',
locationAddressStreet2: '7344 Haley Loop',
locationAddressCity: 'Jacksonstad',
locationAddressCountry: 'United States',
locationAddressPostcode: '32048-5208',
locationAddressState: 'North Dakota',
vetPhonePrimaryPhoneCallingCode: '+33',
vetPhonePrimaryPhoneCountryCode: 'FR',
vetPhonePrimaryPhoneNumber: '789012345',
vetEmailPrimaryEmail: 'john@twenty.com',
vetEmailAdditionalEmails: JSON.stringify([
'tim@twenty.com',
'timapple@twenty.com',
'johnappletim@twenty.com',
]),
birthday: new Date('2019-01-01').toISOString(),
isGoodWithKids: false,
picturesPrimaryLinkUrl:
'https://images.unsplash.com/photo-1507146426996-ef05306b995a?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
picturesPrimaryLinkLabel: 'Picture 1',
picturesSecondaryLinks: JSON.stringify([
{
url: 'https://images.unsplash.com/photo-1447684808650-354ae64db5b8?q=80&w=3267&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
label: 'Picture 2',
},
]),
averageCostOfKibblePerMonthAmountMicros: 2000000000,
averageCostOfKibblePerMonthCurrencyCode: 'USD',
makesOwnerThinkOfFirstName: 'Brad',
makesOwnerThinkOfLastName: 'Pitt',
soundSwag: 'RATING_3',
bio: '[{"id":"c2fc1fe1-8e44-41ce-a670-1819d1520fb1","type":"heading","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left","level":1},"content":[{"type":"text","text":"First encounter","styles":{}}],"children":[]},{"id":"064cb9b6-caf7-440e-8fbd-bcfa332fe909","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"It was a beautiful day; we went to the kennel because a friend of ours told us that the puppies were hoping to find their humans.","styles":{}}],"children":[]},{"id":"45a6c6d9-a561-49e6-b64a-4555dcb72084","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[],"children":[]},{"id":"3dcdfa35-d200-418d-8b67-0c8540c1fa69","type":"heading","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left","level":2},"content":[{"type":"text","text":"TODO","styles":{}}],"children":[]},{"id":"be99fc64-6cd4-4861-a81e-9096d92a6001","type":"checkListItem","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left","checked":true},"content":[{"type":"text","text":"Go to the vet","styles":{}}],"children":[]},{"id":"3ab3777a-4258-4396-8545-8acf19ebc113","type":"checkListItem","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left","checked":false},"content":[{"type":"text","text":"Buy kibbles","styles":{}}],"children":[]},{"id":"5c3a5427-4375-4154-be5a-61dceb55b87e","type":"checkListItem","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left","checked":false},"content":[{"type":"text","text":"Find a cozy spot for the basket","styles":{}}],"children":[]},{"id":"efca1bfb-59a7-4abe-8b71-a9dfd4a866cf","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[],"children":[]},{"id":"b8671315-309c-4da0-8371-8f5dc96ec42f","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"What it looked like when we met :","styles":{}}],"children":[]},{"id":"07758210-8772-4861-8398-a70b044ed42b","type":"image","props":{"backgroundColor":"default","textAlignment":"left","name":"photo-1530667912788-f976e8ee0bd5?q=80&w=3269&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D","url":"https://images.unsplash.com/photo-1530667912788-f976e8ee0bd5?q=80&w=3269&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D","caption":"","showPreview":true,"previewWidth":512},"children":[]},{"id":"a61a47fa-8635-4160-b336-8459cbe15351","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Table of data :","styles":{}}],"children":[]},{"id":"28738ccc-5643-4497-ad66-e2e8c513bdfb","type":"table","props":{"textColor":"default","backgroundColor":"default"},"content":{"type":"tableContent","rows":[{"cells":[[{"type":"text","text":"Header 1","styles":{"bold":true}}],[{"type":"text","text":"Header 2","styles":{"bold":true}}],[{"type":"text","text":"Header 3","styles":{"bold":true}}]]},{"cells":[[{"type":"text","text":"Row 1 - Cell 1","styles":{}}],[{"type":"text","text":"Row 1 - Cell 2","styles":{}}],[{"type":"text","text":"Row 1 - Cell 3","styles":{}}]]}]},"children":[]},{"id":"3599afec-e653-41d8-97b6-c495afa3724e","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[],"children":[]}]',
interestingFacts: [
'Worlds Best Sock Thief',
'Expert at Puppy Eyes',
'Fearless… Except Around Bananas',
'Signature Ear Flip',
],
extraData: JSON.stringify({
settingsOnVetSoftware: {
vetSoftware: 'VetLink',
settings: [
{
key: 'Vet name',
value: 'Dr. John Doe',
},
{
key: 'Vet phone',
value: '234-567-890',
},
{
key: 'Vet email',
value: 'asd@asd.com',
},
{
key: 'Vet address',
value:
'513 Batz Fork, 7344 Haley Loop, Jacksonstad, North Dakota, 32048-5208, United States',
},
],
},
additionalData: [
{
key: 'Weight',
value: '5kg',
},
{
key: 'Height',
value: '30cm',
},
{
key: 'Length',
value: '50cm',
},
{
key: 'Breed',
value: 'Golden Retriever',
},
{
key: 'Color',
value: 'Golden',
},
{
key: 'Eye color',
value: 'Brown',
},
{
key: 'Fur',
value: 'Long',
},
{
key: 'Tail',
value: 'Long',
},
{
key: 'Ears',
value: 'Long',
},
{
key: 'Paws',
value: 'Small',
},
{
key: 'Nose',
value: 'Wet',
},
{
key: 'Teeth',
value: 'White',
},
{
key: 'Habits',
value: 'Barks when someone is at the door',
},
{
key: 'Likes',
value: 'Belly rubs',
},
{
key: 'Dislikes',
value: 'Being alone',
},
],
}),
},
];

View File

@ -0,0 +1,66 @@
type SurveyResultDataSeed = {
id: string;
name: string;
score: number;
percentageOfCompletion: number;
participants: number;
averageEstimatedNumberOfAtomsInTheUniverse: string;
comments: string;
shortNotes: string;
};
export const SURVEY_RESULT_DATA_SEED_COLUMNS: (keyof SurveyResultDataSeed)[] = [
'id',
'name',
'score',
'percentageOfCompletion',
'participants',
'averageEstimatedNumberOfAtomsInTheUniverse',
'comments',
'shortNotes',
];
export const SURVEY_RESULT_DATA_SEED_IDS = {
ID_1: '20202020-0f2a-49d8-8aa2-ec8786153a0b',
ID_2: '20202020-4e83-41ec-93e2-fd70ff09f68c',
ID_3: '20202020-e716-4dd5-ac61-3315bc559e2d',
};
export const SURVEY_RESULT_DATA_SEEDS: SurveyResultDataSeed[] = [
{
id: SURVEY_RESULT_DATA_SEED_IDS.ID_1,
name: 'First survey results - 2021',
score: 0.26022134837694466,
percentageOfCompletion: 76.3561814092,
participants: 599,
averageEstimatedNumberOfAtomsInTheUniverse:
'78667671999742413888718514892176090137414339407788865817757694662213423853',
comments:
'Fuga agnosco patria volva aqua angustus utpote acquiro bestia. Abduco vorax volva agnosco alioqui. Cupiditas abbas aptus uterque bibo sonitus. Tergum carpo degero defaeco. Nostrum verumtamen tactus arbustum tui administratio. Terra sollers calculus blandior. Trans supra tricesimus. Utrum tenus comis adeo asporto sto quibusdam theologus suppono. Cursim casso alveus validus vapulus acer vis. Velum traho adipisci coerceo terminatio at allatus turbo adnuo blandior. Ante consectetur cedo cibo perferendis at amicitia degenero. Doloremque votum cupressus cerno stillicidium arcesso abundans antea sumo. Spoliatio asper solus. Vespillo distinctio ver truculenter torqueo vado aureus. Eaque bonus occaecati defungo defetiscor cibo. Thema ager usus verbum caute tergeo earum adipiscor vinculum. Delinquo tardus canonicus abbas amo confugo doloremque. Comburo quos cumque inflammatio dignissimos abstergo ventus cruentus. Tabula aliquid contego sono delectatio aeternus. Summopere crinis debitis stella conservo desipio termes vulgaris.',
shortNotes: 'verto ascit iure tribuo vulnero',
},
{
id: SURVEY_RESULT_DATA_SEED_IDS.ID_2,
name: 'With only people from the US',
score: 0.07128839939832687,
percentageOfCompletion: 61.6284981836,
participants: 575,
averageEstimatedNumberOfAtomsInTheUniverse:
'58714201303231867082632874445965836504227665636297405101762256297406500076',
comments:
'Benevolentia valens caecus triduana. Cerno curiositas amita. Urbs urbs tertius iure spes succedo aspernatur culpa caute commodo. Cohaero voluptas amplexus denuo caelestis deprimo cresco cognatus aranea. Tabula perferendis ullus taedium vulnero stella corrupti testimonium. Ventosus ars abundans coniuratio. Cohibeo turba apostolus cunae tutamen. Audacia quod benevolentia charisma. Beatus consequuntur uterque crustulum valetudo spes vicinus. Tardus curso crinis ambulo cupiditate cras ad basium volup. Trepide dapifer theologus volva. Urbanus audacia ver aeger clamo animus adsidue error thorax. Ut centum volutabrum virgo summisse earum. Campana bos adulatio candidus tabgo tempore caries coadunatio. Cenaculum absque sustineo angustus quisquam auctus laudantium carbo stella. Conservo thymbra asperiores coaegresco vergo. Cubicularis canis solio. Tener vestrum iure claustrum velum aperte crinis ascisco clarus clam. Vinco ademptio absum. Verto desolo depraedor error coadunatio.',
shortNotes: 'tres tantillus vado aequus ago',
},
{
id: SURVEY_RESULT_DATA_SEED_IDS.ID_3,
name: 'People who like cats',
score: 0.1480973360594362,
percentageOfCompletion: 72.4289541366,
participants: 590,
averageEstimatedNumberOfAtomsInTheUniverse:
'51790645279092482632713239096036038617511762830423654232543291815995800207',
comments:
'Omnis careo ait praesentium inventore amet stips versus. Charisma demens vergo ex tibi desolo harum valens urbs abeo. Subnecto tantum tenax. Esse aduro caste comprehendo color bellicus excepturi tego coniuratio stella. Cogo cognatus cogo acerbitas aro. Asper cohibeo tam venustas arma antea studio eveniet casus. Numquam benevolentia voco celer. Defungo arcesso viridis veritas adsuesco desparatus patria tondeo canonicus stillicidium. Clamo truculenter vix allatus succedo depereo aestivus praesentium. Spiritus comes adipisci. Atrocitas virtus alveus strenuus. Repellat thermae aptus placeat aut. Ambitus tunc convoco adulatio averto. Agnitio aegrotatio aequus decumbo conventus. Valens adulatio ad voluptatibus conspergo vallum tredecim correptius. Celo aranea umquam. A abscido vigor virtus tristis cavus. Truculenter natus bonus sollers vulgivagus amita. Occaecati illo voco terga carcer commodo. Succurro vociferor bene vere accusamus defluo at videlicet aranea deleniti.',
shortNotes: 'aeneus armarium conventus curto rerum',
},
];

View File

@ -0,0 +1,58 @@
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
type WorkspaceMemberDataSeed = {
id: string;
nameFirstName: string;
nameLastName: string;
locale: string;
colorScheme: string;
userEmail: string;
userId: string;
};
export const WORKSPACE_MEMBER_DATA_SEED_COLUMNS: (keyof WorkspaceMemberDataSeed)[] =
[
'id',
'nameFirstName',
'nameLastName',
'locale',
'colorScheme',
'userEmail',
'userId',
];
export const WORKSPACE_MEMBER_DATA_SEED_IDS = {
TIM: '20202020-0687-4c41-b707-ed1bfca972a7',
JONY: '20202020-77d5-4cb6-b60a-f4a835a85d61',
PHIL: '20202020-1553-45c6-a028-5a9064cce07f',
};
export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
{
id: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
nameFirstName: 'Tim',
nameLastName: 'Apple',
locale: 'en',
colorScheme: 'Light',
userEmail: 'tim@apple.dev',
userId: USER_DATA_SEED_IDS.TIM,
},
{
id: WORKSPACE_MEMBER_DATA_SEED_IDS.JONY,
nameFirstName: 'Jony',
nameLastName: 'Ive',
locale: 'en',
colorScheme: 'Light',
userEmail: 'jony.ive@apple.dev',
userId: USER_DATA_SEED_IDS.JONY,
},
{
id: WORKSPACE_MEMBER_DATA_SEED_IDS.PHIL,
nameFirstName: 'Phil',
nameLastName: 'Schiler',
locale: 'en',
colorScheme: 'Light',
userEmail: 'phil.schiler@apple.dev',
userId: USER_DATA_SEED_IDS.PHIL,
},
];

View File

@ -0,0 +1,250 @@
import { Injectable } from '@nestjs/common';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import {
API_KEY_DATA_SEED_COLUMNS,
API_KEY_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/api-key-data-seeds.constant';
import {
CALENDAR_CHANNEL_DATA_SEED_COLUMNS,
CALENDAR_CHANNEL_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-channel-data-seeds.constant';
import {
CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEED_COLUMNS,
CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-channel-event-association-data-seeds.constant';
import {
CALENDAR_EVENT_DATA_SEED_COLUMNS,
CALENDAR_EVENT_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
import {
CALENDAR_EVENT_PARTICIPANT_DATA_SEED_COLUMNS,
CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-participant-data-seeds.constant';
import {
COMPANY_DATA_SEED_COLUMNS,
COMPANY_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/company-data-seeds.constant';
import {
CONNECTED_ACCOUNT_DATA_SEED_COLUMNS,
CONNECTED_ACCOUNT_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/connected-account-data-seeds.constant';
import {
MESSAGE_CHANNEL_DATA_SEED_COLUMNS,
MESSAGE_CHANNEL_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant';
import {
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_COLUMNS,
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-channel-message-association-data-seeds.constant';
import {
MESSAGE_DATA_SEED_COLUMNS,
MESSAGE_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
import {
MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS,
MESSAGE_PARTICIPANT_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant';
import {
MESSAGE_THREAD_DATA_SEED_COLUMNS,
MESSAGE_THREAD_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-thread-data-seeds.constant';
import {
OPPORTUNITY_DATA_SEED_COLUMNS,
OPPORTUNITY_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/opportunity-data-seeds.constant';
import {
PERSON_DATA_SEED_COLUMNS,
PERSON_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import {
PET_DATA_SEED_COLUMNS,
PET_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/pet-data-seeds.constant';
import {
SURVEY_RESULT_DATA_SEED_COLUMNS,
SURVEY_RESULT_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/survey-result-data-seeds.constant';
import {
WORKSPACE_MEMBER_DATA_SEED_COLUMNS,
WORKSPACE_MEMBER_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
import { prefillViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-views';
import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites';
const RECORD_SEEDS_CONFIGS = [
{
tableName: 'workspaceMember',
pgColumns: WORKSPACE_MEMBER_DATA_SEED_COLUMNS,
recordSeeds: WORKSPACE_MEMBER_DATA_SEEDS,
},
{
tableName: 'company',
pgColumns: COMPANY_DATA_SEED_COLUMNS,
recordSeeds: COMPANY_DATA_SEEDS,
},
{
tableName: 'person',
pgColumns: PERSON_DATA_SEED_COLUMNS,
recordSeeds: PERSON_DATA_SEEDS,
},
{
tableName: 'opportunity',
pgColumns: OPPORTUNITY_DATA_SEED_COLUMNS,
recordSeeds: OPPORTUNITY_DATA_SEEDS,
},
{
tableName: 'apiKey',
pgColumns: API_KEY_DATA_SEED_COLUMNS,
recordSeeds: API_KEY_DATA_SEEDS,
},
{
tableName: 'connectedAccount',
pgColumns: CONNECTED_ACCOUNT_DATA_SEED_COLUMNS,
recordSeeds: CONNECTED_ACCOUNT_DATA_SEEDS,
},
{
tableName: 'calendarChannel',
pgColumns: CALENDAR_CHANNEL_DATA_SEED_COLUMNS,
recordSeeds: CALENDAR_CHANNEL_DATA_SEEDS,
},
{
tableName: 'calendarEvent',
pgColumns: CALENDAR_EVENT_DATA_SEED_COLUMNS,
recordSeeds: CALENDAR_EVENT_DATA_SEEDS,
},
{
tableName: 'calendarChannelEventAssociation',
pgColumns: CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEED_COLUMNS,
recordSeeds: CALENDAR_CHANNEL_EVENT_ASSOCIATION_DATA_SEEDS,
},
{
tableName: 'calendarEventParticipant',
pgColumns: CALENDAR_EVENT_PARTICIPANT_DATA_SEED_COLUMNS,
recordSeeds: CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS,
},
{
tableName: 'messageChannel',
pgColumns: MESSAGE_CHANNEL_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_CHANNEL_DATA_SEEDS,
},
{
tableName: 'messageThread',
pgColumns: MESSAGE_THREAD_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_THREAD_DATA_SEEDS,
},
{
tableName: 'message',
pgColumns: MESSAGE_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_DATA_SEEDS,
},
{
tableName: 'messageChannelMessageAssociation',
pgColumns: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_DATA_SEEDS,
},
{
tableName: 'messageParticipant',
pgColumns: MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS,
recordSeeds: MESSAGE_PARTICIPANT_DATA_SEEDS,
},
{
tableName: '_pet',
pgColumns: PET_DATA_SEED_COLUMNS,
recordSeeds: PET_DATA_SEEDS,
},
{
tableName: '_surveyResult',
pgColumns: SURVEY_RESULT_DATA_SEED_COLUMNS,
recordSeeds: SURVEY_RESULT_DATA_SEEDS,
},
];
@Injectable()
export class DevSeederDataService {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly objectMetadataService: ObjectMetadataService,
) {}
public async seed({
schemaName,
workspaceId,
}: {
schemaName: string;
workspaceId: string;
}) {
const mainDataSource =
await this.workspaceDataSourceService.connectToMainDataSource();
if (!mainDataSource) {
throw new Error('Could not connect to main data source');
}
const objectMetadataItems =
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
await mainDataSource.transaction(
async (entityManager: WorkspaceEntityManager) => {
for (const recordSeedsConfig of RECORD_SEEDS_CONFIGS) {
await this.seedRecords({
entityManager,
schemaName,
tableName: recordSeedsConfig.tableName,
pgColumns: recordSeedsConfig.pgColumns,
recordSeeds: recordSeedsConfig.recordSeeds,
});
}
const viewDefinitionsWithId = await prefillViews(
entityManager,
schemaName,
objectMetadataItems,
);
await prefillWorkspaceFavorites(
viewDefinitionsWithId
.filter(
(view) =>
view.key === 'INDEX' &&
shouldSeedWorkspaceFavorite(
view.objectMetadataId,
objectMetadataItems,
),
)
.map((view) => view.id),
entityManager,
schemaName,
);
},
);
}
private async seedRecords({
entityManager,
schemaName,
tableName,
pgColumns,
recordSeeds,
}: {
entityManager: WorkspaceEntityManager;
schemaName: string;
tableName: string;
pgColumns: string[];
recordSeeds: Record<string, unknown>[];
}) {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, pgColumns)
.orIgnore()
.values(recordSeeds)
.returning('*')
.execute();
}
}

View File

@ -0,0 +1,42 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { Workspace } 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 { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { DevSeederPermissionsService } from 'src/engine/workspace-manager/dev-seeder/core/services/dev-seeder-permissions.service';
import { DevSeederDataService } from 'src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service';
import { DevSeederMetadataService } from 'src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service';
import { DevSeederService } from 'src/engine/workspace-manager/dev-seeder/services/dev-seeder.service';
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
@Module({
imports: [
ObjectMetadataModule,
FieldMetadataModule,
WorkspaceDataSourceModule,
WorkspaceCacheStorageModule,
TypeORMModule,
DataSourceModule,
RoleModule,
UserRoleModule,
FeatureFlagModule,
WorkspaceSyncMetadataModule,
TypeOrmModule.forFeature([Workspace], 'core'),
],
exports: [DevSeederService],
providers: [
DevSeederService,
DevSeederMetadataService,
DevSeederPermissionsService,
DevSeederDataService,
],
})
export class DevSeederModule {}

View File

@ -0,0 +1,68 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
export const COMPANY_CUSTOM_FIELD_SEEDS: FieldMetadataSeed[] = [
{
type: FieldMetadataType.TEXT,
name: 'tagline',
label: 'Tagline',
description: "Company's Tagline",
icon: 'IconAdCircle',
isActive: true,
isNullable: false,
isUnique: false,
defaultValue: "''",
},
{
type: FieldMetadataType.LINKS,
name: 'introVideo',
label: 'Intro Video',
description: "Company's Intro Video",
icon: 'IconVideo',
isActive: true,
isNullable: true,
isUnique: false,
},
{
type: FieldMetadataType.MULTI_SELECT,
name: 'workPolicy',
label: 'Work Policy',
description: "Company's Work Policy",
icon: 'IconHome',
isActive: true,
isNullable: true,
isUnique: false,
options: [
{
color: 'green',
label: 'On-Site',
position: 0,
value: 'ON_SITE',
},
{
color: 'turquoise',
label: 'Hybrid',
position: 1,
value: 'HYBRID',
},
{
color: 'sky',
label: 'Remote Work',
position: 2,
value: 'REMOTE_WORK',
},
],
},
{
type: FieldMetadataType.BOOLEAN,
name: 'visaSponsorship',
label: 'Visa Sponsorship',
description: "Company's Visa Sponsorship Policy",
icon: 'IconBrandVisa',
isActive: true,
isNullable: true,
isUnique: false,
defaultValue: false,
},
];

View File

@ -0,0 +1,99 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
export const PERSON_CUSTOM_FIELD_SEEDS: FieldMetadataSeed[] = [
{
type: FieldMetadataType.TEXT,
name: 'intro',
label: 'Intro',
description: "Contact's Intro",
icon: 'IconNote',
isActive: true,
isNullable: true,
isUnique: false,
},
{
type: FieldMetadataType.PHONES,
name: 'whatsapp',
label: 'Whatsapp',
description: "Contact's Whatsapp Number",
icon: 'IconBrandWhatsapp',
isActive: true,
isNullable: false,
isUnique: false,
defaultValue: {
primaryPhoneNumber: "''",
primaryPhoneCountryCode: "'FR'",
primaryPhoneCallingCode: "'+33'",
additionalPhones: null,
},
},
{
type: FieldMetadataType.MULTI_SELECT,
name: 'workPreference',
label: 'Work Preference',
description: "Person's Work Preference",
icon: 'IconHome',
isActive: true,
isNullable: true,
isUnique: false,
options: [
{
color: 'green',
label: 'On-Site',
position: 0,
value: 'ON_SITE',
},
{
color: 'turquoise',
label: 'Hybrid',
position: 1,
value: 'HYBRID',
},
{
color: 'sky',
label: 'Remote Work',
position: 2,
value: 'REMOTE_WORK',
},
],
},
{
type: FieldMetadataType.RATING,
name: 'performanceRating',
label: 'Performance Rating',
description: "Person's Performance Rating",
icon: 'IconStars',
isActive: true,
isNullable: true,
isUnique: false,
options: [
{
label: '1',
value: 'RATING_1',
position: 0,
},
{
label: '2',
value: 'RATING_2',
position: 1,
},
{
label: '3',
value: 'RATING_3',
position: 2,
},
{
label: '4',
value: 'RATING_4',
position: 3,
},
{
label: '5',
value: 'RATING_5',
position: 4,
},
],
},
];

View File

@ -0,0 +1,107 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
export const PET_CUSTOM_FIELD_SEEDS: FieldMetadataSeed[] = [
{
type: FieldMetadataType.SELECT,
label: 'Species',
name: 'species',
options: [
{ label: 'Dog', value: 'DOG', position: 0, color: 'blue' },
{ label: 'Cat', value: 'CAT', position: 1, color: 'red' },
{ label: 'Bird', value: 'BIRD', position: 2, color: 'green' },
{ label: 'Fish', value: 'FISH', position: 3, color: 'yellow' },
{ label: 'Rabbit', value: 'RABBIT', position: 4, color: 'purple' },
{ label: 'Hamster', value: 'HAMSTER', position: 5, color: 'orange' },
],
},
{
type: FieldMetadataType.MULTI_SELECT,
label: 'Traits',
name: 'traits',
options: [
{ label: 'Playful', value: 'PLAYFUL', position: 0, color: 'blue' },
{ label: 'Friendly', value: 'FRIENDLY', position: 1, color: 'red' },
{
label: 'Protective',
value: 'PROTECTIVE',
position: 2,
color: 'green',
},
{ label: 'Shy', value: 'SHY', position: 3, color: 'yellow' },
{ label: 'Brave', value: 'BRAVE', position: 4, color: 'purple' },
{ label: 'Curious', value: 'CURIOUS', position: 5, color: 'orange' },
],
},
{
type: FieldMetadataType.TEXT,
label: 'Comments',
name: 'comments',
},
{
type: FieldMetadataType.NUMBER,
label: 'Age',
name: 'age',
},
{
type: FieldMetadataType.ADDRESS,
label: 'Location',
name: 'location',
},
{
type: FieldMetadataType.PHONES,
label: 'Vet phone',
name: 'vetPhone',
},
{
type: FieldMetadataType.EMAILS,
label: 'Vet email',
name: 'vetEmail',
},
{
type: FieldMetadataType.DATE,
label: 'Birthday',
name: 'birthday',
},
{
type: FieldMetadataType.BOOLEAN,
label: 'Is good with kids',
name: 'isGoodWithKids',
},
{
type: FieldMetadataType.LINKS,
label: 'Pictures',
name: 'pictures',
},
{
type: FieldMetadataType.CURRENCY,
label: 'Average cost of kibble per month',
name: 'averageCostOfKibblePerMonth',
},
{
type: FieldMetadataType.FULL_NAME,
label: 'Makes its owner think of',
name: 'makesOwnerThinkOf',
},
{
type: FieldMetadataType.RATING,
label: 'Sound swag (bark style, meow style, etc.)',
name: 'soundSwag',
},
{
type: FieldMetadataType.RICH_TEXT,
label: 'Bio',
name: 'bio',
},
{
type: FieldMetadataType.ARRAY,
label: 'Interesting facts',
name: 'interestingFacts',
},
{
type: FieldMetadataType.RAW_JSON,
label: 'Extra data',
name: 'extraData',
},
];

View File

@ -0,0 +1,63 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { NumberDataType } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
import { FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
export const SURVEY_RESULT_CUSTOM_FIELD_SEEDS: FieldMetadataSeed[] = [
{
type: FieldMetadataType.NUMBER,
label: 'Score (Float 3 decimals)',
name: 'score',
settings: {
dataType: NumberDataType.FLOAT,
decimals: 3,
type: 'number',
},
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
{
type: FieldMetadataType.NUMBER,
label: 'Percentage of completion (Float 3 decimals + percentage)',
name: 'percentageOfCompletion',
settings: {
dataType: NumberDataType.FLOAT,
decimals: 6,
type: 'percentage',
},
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
{
type: FieldMetadataType.NUMBER,
label: 'Participants (Int)',
name: 'participants',
settings: {
dataType: NumberDataType.INT,
type: 'number',
},
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
{
type: FieldMetadataType.NUMBER,
label: 'Average estimated number of atoms in the universe (BigInt)',
name: 'averageEstimatedNumberOfAtomsInTheUniverse',
settings: {
dataType: NumberDataType.BIGINT,
type: 'number',
},
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
{
type: FieldMetadataType.TEXT,
label: 'Comments (Max 5 rows)',
name: 'comments',
settings: {
displayedMaxRows: 5,
},
} as FieldMetadataDTO<FieldMetadataType.TEXT>,
{
type: FieldMetadataType.TEXT,
label: 'Short notes (Max 1 row)',
name: 'shortNotes',
settings: {
displayedMaxRows: 1,
},
} as FieldMetadataDTO<FieldMetadataType.TEXT>,
];

View File

@ -0,0 +1,9 @@
import { ObjectMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/object-metadata-seed.type';
export const PET_CUSTOM_OBJECT_SEED: ObjectMetadataSeed = {
labelPlural: 'Pets',
labelSingular: 'Pet',
namePlural: 'pets',
nameSingular: 'pet',
icon: 'IconCat',
};

View File

@ -0,0 +1,11 @@
import { ObjectMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/object-metadata-seed.type';
export const ROCKET_CUSTOM_OBJECT_SEED: ObjectMetadataSeed = {
labelPlural: 'Rockets',
labelSingular: 'Rocket',
namePlural: 'rockets',
nameSingular: 'rocket',
icon: 'IconRocket',
description: 'A rocket',
isRemote: false,
};

View File

@ -0,0 +1,9 @@
import { ObjectMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/object-metadata-seed.type';
export const SURVEY_RESULT_CUSTOM_OBJECT_SEED: ObjectMetadataSeed = {
labelPlural: 'Survey results',
labelSingular: 'Survey result',
namePlural: 'surveyResults',
nameSingular: 'surveyResult',
icon: 'IconRulerMeasure',
};

View File

@ -0,0 +1,115 @@
import { Injectable } from '@nestjs/common';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { COMPANY_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/company-custom-field-seeds.constant';
import { PERSON_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/person-custom-field-seeds.constant';
import { PET_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-field-seeds.constant';
import { SURVEY_RESULT_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/survey-results-field-seeds.constant';
import { PET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/pet-custom-object-seed.constant';
import { ROCKET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/rocket-custom-object-seed.constant';
import { SURVEY_RESULT_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/survey-results-object-seed.constant';
import { FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
import { ObjectMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/object-metadata-seed.type';
@Injectable()
export class DevSeederMetadataService {
constructor(
private readonly objectMetadataService: ObjectMetadataService,
private readonly fieldMetadataService: FieldMetadataService,
) {}
public async seed({
dataSourceMetadata,
workspaceId,
}: {
dataSourceMetadata: DataSourceEntity;
workspaceId: string;
}) {
await this.seedCustomObject({
dataSourceId: dataSourceMetadata.id,
workspaceId,
objectMetadataSeed: ROCKET_CUSTOM_OBJECT_SEED,
});
await this.seedCustomObject({
dataSourceId: dataSourceMetadata.id,
workspaceId,
objectMetadataSeed: PET_CUSTOM_OBJECT_SEED,
});
await this.seedCustomFields({
workspaceId,
objectMetadataNameSingular: PET_CUSTOM_OBJECT_SEED.nameSingular,
fieldMetadataSeeds: PET_CUSTOM_FIELD_SEEDS,
});
await this.seedCustomObject({
dataSourceId: dataSourceMetadata.id,
workspaceId,
objectMetadataSeed: SURVEY_RESULT_CUSTOM_OBJECT_SEED,
});
await this.seedCustomFields({
workspaceId,
objectMetadataNameSingular: SURVEY_RESULT_CUSTOM_OBJECT_SEED.nameSingular,
fieldMetadataSeeds: SURVEY_RESULT_CUSTOM_FIELD_SEEDS,
});
await this.seedCustomFields({
workspaceId,
objectMetadataNameSingular: 'company',
fieldMetadataSeeds: COMPANY_CUSTOM_FIELD_SEEDS,
});
await this.seedCustomFields({
workspaceId,
objectMetadataNameSingular: 'person',
fieldMetadataSeeds: PERSON_CUSTOM_FIELD_SEEDS,
});
}
private async seedCustomObject({
dataSourceId,
workspaceId,
objectMetadataSeed,
}: {
dataSourceId: string;
workspaceId: string;
objectMetadataSeed: ObjectMetadataSeed;
}): Promise<void> {
await this.objectMetadataService.createOne({
...objectMetadataSeed,
dataSourceId,
workspaceId,
});
}
private async seedCustomFields({
workspaceId,
objectMetadataNameSingular,
fieldMetadataSeeds,
}: {
workspaceId: string;
objectMetadataNameSingular: string;
fieldMetadataSeeds: FieldMetadataSeed[];
}): Promise<void> {
const objectMetadata =
await this.objectMetadataService.findOneWithinWorkspace(workspaceId, {
where: { nameSingular: objectMetadataNameSingular },
});
if (!objectMetadata) {
throw new Error('Object metadata not found');
}
await this.fieldMetadataService.createMany(
fieldMetadataSeeds.map((fieldMetadataSeed) => ({
...fieldMetadataSeed,
objectMetadataId: objectMetadata.id,
workspaceId,
})),
);
}
}

View File

@ -0,0 +1,6 @@
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
export type FieldMetadataSeed = Omit<
CreateFieldInput,
'objectMetadataId' | 'workspaceId'
>;

View File

@ -0,0 +1,6 @@
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
export type ObjectMetadataSeed = Omit<
CreateObjectInput,
'workspaceId' | 'dataSourceId' | 'fields'
>;

View File

@ -0,0 +1,83 @@
import { Injectable, Logger } from '@nestjs/common';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { DevSeederPermissionsService } from 'src/engine/workspace-manager/dev-seeder/core/services/dev-seeder-permissions.service';
import { seedCoreSchema } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-core-schema.util';
import { DevSeederDataService } from 'src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service';
import { DevSeederMetadataService } from 'src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@Injectable()
export class DevSeederService {
private readonly logger = new Logger(DevSeederService.name);
constructor(
private readonly typeORMService: TypeORMService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly twentyConfigService: TwentyConfigService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly dataSourceService: DataSourceService,
private readonly featureFlagService: FeatureFlagService,
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
private readonly devSeederMetadataService: DevSeederMetadataService,
private readonly devSeederPermissionsService: DevSeederPermissionsService,
private readonly devSeederDataService: DevSeederDataService,
) {}
public async seedDev(workspaceId: string): Promise<void> {
const mainDataSource = this.typeORMService.getMainDataSource();
if (!mainDataSource) {
throw new Error('Could not connect to workspace data source');
}
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
const appVersion = this.twentyConfigService.get('APP_VERSION');
await seedCoreSchema({
dataSource: mainDataSource,
workspaceId,
seedBilling: isBillingEnabled,
appVersion,
});
const schemaName =
await this.workspaceDataSourceService.createWorkspaceDBSchema(
workspaceId,
);
const dataSourceMetadata =
await this.dataSourceService.createDataSourceMetadata(
workspaceId,
schemaName,
);
const featureFlags =
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
await this.workspaceSyncMetadataService.synchronize({
workspaceId: workspaceId,
dataSourceId: dataSourceMetadata.id,
featureFlags,
});
await this.devSeederMetadataService.seed({
dataSourceMetadata,
workspaceId,
});
await this.devSeederPermissionsService.initPermissions(workspaceId);
await this.devSeederDataService.seed({
schemaName: dataSourceMetadata.schema,
workspaceId,
});
await this.workspaceCacheStorageService.flush(workspaceId, undefined);
}
}

View File

@ -7,7 +7,7 @@ export const STRIPE_ID = '1f70157c-4ea5-4d81-bc49-e1401abfbb94';
export const FIGMA_ID = '9d5bcf43-7d38-4e88-82cb-d6d4ce638bf0';
export const NOTION_ID = '06290608-8bf0-4806-99ae-a715a6a93fad';
export const companyPrefillData = async (
export const prefillCompanies = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {

View File

@ -6,10 +6,9 @@ import {
FIGMA_ID,
NOTION_ID,
STRIPE_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/company';
} from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies';
// FixMe: Is this file a duplicate of src/database/typeorm-seeds/workspace/people.ts
export const personPrefillData = async (
export const prefillPeople = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {

View File

@ -1,9 +1,53 @@
import { v4 } from 'uuid';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { ViewDefinition } from 'src/engine/workspace-manager/standard-objects-prefill-data/types/view-definition.interface';
import { companiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view';
import { customAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/custom-all.view';
import { notesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view';
import { opportunitiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view';
import { opportunitiesByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view';
import { peopleAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view';
import { tasksAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view';
import { tasksAssignedToMeView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-assigned-to-me';
import { tasksByStatusView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view';
import { workflowRunsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view';
import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view';
import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view';
export const createWorkspaceViews = async (
export const prefillViews = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const customObjectMetadataItems = objectMetadataItems.filter(
(item) => item.isCustom,
);
const customViews = customObjectMetadataItems.map((item) =>
customAllView(item),
);
const views = [
companiesAllView(objectMetadataItems),
peopleAllView(objectMetadataItems),
opportunitiesAllView(objectMetadataItems),
opportunitiesByStageView(objectMetadataItems),
notesAllView(objectMetadataItems),
tasksAllView(objectMetadataItems),
tasksAssignedToMeView(objectMetadataItems),
tasksByStatusView(objectMetadataItems),
workflowsAllView(objectMetadataItems),
workflowVersionsAllView(objectMetadataItems),
workflowRunsAllView(objectMetadataItems),
...customViews,
];
return createWorkspaceViews(entityManager, schemaName, views);
};
const createWorkspaceViews = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
viewDefinitions: ViewDefinition[],

View File

@ -0,0 +1,26 @@
import { v4 } from 'uuid';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'favorite';
export const prefillWorkspaceFavorites = async (
viewIds: string[],
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'viewId', 'position'])
.values(
viewIds.map((viewId, index) => ({
id: v4(),
viewId,
position: index,
})),
)
.execute();
};

View File

@ -1,37 +0,0 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views';
import { companiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view';
import { notesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view';
import { opportunitiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view';
import { opportunitiesByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view';
import { peopleAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view';
import { tasksAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view';
import { tasksAssignedToMeView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-assigned-to-me';
import { tasksByStatusView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view';
import { workflowRunsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view';
import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view';
import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view';
export const seedViewWithDemoData = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
) => {
const viewDefinitions = [
companiesAllView(objectMetadataStandardIdToIdMap),
peopleAllView(objectMetadataStandardIdToIdMap),
opportunitiesAllView(objectMetadataStandardIdToIdMap),
opportunitiesByStageView(objectMetadataStandardIdToIdMap),
notesAllView(objectMetadataStandardIdToIdMap),
tasksAllView(objectMetadataStandardIdToIdMap),
tasksAssignedToMeView(objectMetadataStandardIdToIdMap),
tasksByStatusView(objectMetadataStandardIdToIdMap),
workflowsAllView(objectMetadataStandardIdToIdMap),
workflowVersionsAllView(objectMetadataStandardIdToIdMap),
workflowRunsAllView(objectMetadataStandardIdToIdMap),
];
return createWorkspaceViews(entityManager, schemaName, viewDefinitions);
};

View File

@ -1,58 +1,35 @@
import { DataSource } from 'typeorm';
import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
import { companyPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/company';
import { personPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/person';
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
import { prefillCompanies } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies';
import { prefillPeople } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-people';
import { prefillViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-views';
import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites';
export const standardObjectsPrefillData = async (
mainDataSource: DataSource,
schemaName: string,
objectMetadata: ObjectMetadataEntity[],
objectMetadataItems: ObjectMetadataEntity[],
) => {
const objectMetadataMap = objectMetadata.reduce((acc, object) => {
if (!object.standardId) {
throw new Error('Standard Id is not set for object: ${object.name}');
}
// @ts-expect-error legacy noImplicitAny
acc[object.standardId] = {
id: object.id,
fields: object.fields.reduce((acc, field) => {
if (!field.standardId) {
throw new Error('Standard Id is not set for field: ${field.name}');
}
// @ts-expect-error legacy noImplicitAny
acc[field.standardId] = field.id;
return acc;
}, {}),
};
return acc;
}, {});
mainDataSource.transaction(async (entityManager: WorkspaceEntityManager) => {
await companyPrefillData(entityManager, schemaName);
await personPrefillData(entityManager, schemaName);
const viewDefinitionsWithId = await seedViewWithDemoData(
await prefillCompanies(entityManager, schemaName);
await prefillPeople(entityManager, schemaName);
const viewDefinitionsWithId = await prefillViews(
entityManager,
schemaName,
objectMetadataMap,
objectMetadataItems,
);
await seedWorkspaceFavorites(
await prefillWorkspaceFavorites(
viewDefinitionsWithId
.filter(
(view) =>
view.key === 'INDEX' &&
shouldSeedWorkspaceFavorite(
view.objectMetadataId,
objectMetadataMap,
objectMetadataItems,
),
)
.map((view) => view.id),

View File

@ -1,6 +1,5 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
COMPANY_STANDARD_FIELD_IDS,
@ -8,12 +7,19 @@ import {
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const companiesAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const companyObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.company,
);
if (!companyObjectMetadata) {
throw new Error('Company object metadata not found');
}
return {
name: 'All Companies',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].id,
objectMetadataId: companyObjectMetadata.id ?? '',
type: 'table',
key: 'INDEX',
position: 0,
@ -23,18 +29,23 @@ export const companiesAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.name
],
companyObjectMetadata.fields.find(
(field) => field.standardId === COMPANY_STANDARD_FIELD_IDS.name,
)?.id ??
'' ??
'',
position: 0,
isVisible: true,
size: 180,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.domainName
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === COMPANY_STANDARD_FIELD_IDS.domainName,
)?.id ??
'' ??
'',
position: 1,
isVisible: true,
size: 100,
@ -42,36 +53,40 @@ export const companiesAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.createdBy
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === COMPANY_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.accountOwner
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === COMPANY_STANDARD_FIELD_IDS.accountOwner,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.employees
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === COMPANY_STANDARD_FIELD_IDS.employees,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
@ -79,9 +94,10 @@ export const companiesAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.linkedinLink
],
companyObjectMetadata.fields.find(
(field) =>
field.standardId === COMPANY_STANDARD_FIELD_IDS.linkedinLink,
)?.id ?? '',
position: 6,
isVisible: true,
size: 170,
@ -89,9 +105,9 @@ export const companiesAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].fields[
COMPANY_STANDARD_FIELD_IDS.address
],
companyObjectMetadata.fields.find(
(field) => field.standardId === COMPANY_STANDARD_FIELD_IDS.address,
)?.id ?? '',
position: 7,
isVisible: true,
size: 170,

View File

@ -0,0 +1,44 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
export const customAllView = (objectMetadataItem: ObjectMetadataEntity) => {
const nameField = objectMetadataItem.fields.find(
(field) => field.name === 'name',
);
const otherFields = objectMetadataItem.fields.filter(
(field) => field.name !== 'name',
);
if (!nameField) {
throw new Error(
`Name field not found while creating All ${objectMetadataItem.namePlural} view`,
);
}
return {
name: `All ${objectMetadataItem.namePlural}`,
objectMetadataId: objectMetadataItem.id,
type: 'table',
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
fieldMetadataId:
objectMetadataItem.fields.find((field) => field.name === 'name')
?.id ?? '',
position: 0,
isVisible: true,
size: 180,
},
...otherFields.map((field, index) => ({
fieldMetadataId: field.id,
position: index + 1,
isVisible: true,
size: 180,
})),
],
};
};

View File

@ -1,18 +1,22 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
NOTE_STANDARD_FIELD_IDS,
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const notesAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
) => {
export const notesAllView = (objectMetadataItems: ObjectMetadataEntity[]) => {
const noteObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.note,
);
if (!noteObjectMetadata) {
throw new Error('Note object metadata not found');
}
return {
name: 'All Notes',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].id,
objectMetadataId: noteObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -22,45 +26,46 @@ export const notesAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].fields[
NOTE_STANDARD_FIELD_IDS.title
],
noteObjectMetadata.fields.find(
(field) => field.standardId === NOTE_STANDARD_FIELD_IDS.title,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].fields[
NOTE_STANDARD_FIELD_IDS.noteTargets
],
noteObjectMetadata.fields.find(
(field) => field.standardId === NOTE_STANDARD_FIELD_IDS.noteTargets,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].fields[
NOTE_STANDARD_FIELD_IDS.bodyV2
],
noteObjectMetadata.fields.find(
(field) => field.standardId === NOTE_STANDARD_FIELD_IDS.bodyV2,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].fields[
NOTE_STANDARD_FIELD_IDS.createdBy
],
noteObjectMetadata.fields.find(
(field) => field.standardId === NOTE_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.note].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
noteObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,

View File

@ -1,16 +1,22 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const opportunitiesAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const opportunityObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.opportunity,
);
if (!opportunityObjectMetadata) {
throw new Error('Opportunity object metadata not found');
}
return {
name: 'All Opportunities',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].id,
objectMetadataId: opportunityObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -20,16 +26,19 @@ export const opportunitiesAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.name],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.amount],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.amount,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
@ -37,16 +46,20 @@ export const opportunitiesAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.createdBy],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.closeDate],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.closeDate,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
@ -54,16 +67,21 @@ export const opportunitiesAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.company],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.company,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId ===
OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,

View File

@ -1,75 +1,93 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const opportunitiesByStageView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const opportunityObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.opportunity,
);
if (!opportunityObjectMetadata) {
throw new Error('Opportunity object metadata not found');
}
return {
name: 'By Stage',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].id,
objectMetadataId: opportunityObjectMetadata.id,
type: 'kanban',
key: null,
position: 2,
icon: 'IconLayoutKanban',
kanbanFieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].fields[
OPPORTUNITY_STANDARD_FIELD_IDS.stage
],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
kanbanAggregateOperation: AggregateOperations.MIN,
kanbanAggregateOperationFieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].fields[
OPPORTUNITY_STANDARD_FIELD_IDS.amount
],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.amount,
)?.id ?? '',
filters: [],
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.name],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.amount],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.amount,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.createdBy],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.closeDate],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.closeDate,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.company],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.company,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId ===
OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
@ -78,40 +96,50 @@ export const opportunitiesByStageView = (
groups: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'NEW',
position: 0,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'SCREENING',
position: 1,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'MEETING',
position: 2,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'PROPOSAL',
position: 3,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'CUSTOMER',
position: 4,

View File

@ -1,38 +1,47 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const opportunitiesTableByStageView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const opportunityObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.opportunity,
);
if (!opportunityObjectMetadata) {
throw new Error('Opportunity object metadata not found');
}
return {
name: 'By Stage',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].id,
objectMetadataId: opportunityObjectMetadata.id,
type: 'table',
key: null,
position: 1,
icon: 'IconList',
kanbanFieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity].fields[
OPPORTUNITY_STANDARD_FIELD_IDS.stage
],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
filters: [],
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.name],
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.amount],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.amount,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
@ -40,16 +49,20 @@ export const opportunitiesTableByStageView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.createdBy],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.closeDate],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.closeDate,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
@ -57,8 +70,10 @@ export const opportunitiesTableByStageView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.company],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.company,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
@ -66,8 +81,11 @@ export const opportunitiesTableByStageView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId ===
OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
@ -76,40 +94,50 @@ export const opportunitiesTableByStageView = (
groups: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'NEW',
position: 0,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'SCREENING',
position: 1,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'MEETING',
position: 2,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'PROPOSAL',
position: 3,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.opportunity]
.fields[OPPORTUNITY_STANDARD_FIELD_IDS.stage],
opportunityObjectMetadata.fields.find(
(field) =>
field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
isVisible: true,
fieldValue: 'CUSTOMER',
position: 4,

View File

@ -1,19 +1,23 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
PERSON_STANDARD_FIELD_IDS,
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const peopleAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
) => {
export const peopleAllView = (objectMetadataItems: ObjectMetadataEntity[]) => {
const personObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.person,
);
if (!personObjectMetadata) {
throw new Error('Person object metadata not found');
}
return {
name: 'All People',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].id,
objectMetadataId: personObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -23,18 +27,18 @@ export const peopleAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.name
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.emails
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.emails,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
@ -42,27 +46,27 @@ export const peopleAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.createdBy
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.company
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.company,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.phones
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.phones,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
@ -70,9 +74,10 @@ export const peopleAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
personObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
@ -80,36 +85,37 @@ export const peopleAllView = (
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.city
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.city,
)?.id ?? '',
position: 6,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.jobTitle
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.jobTitle,
)?.id ?? '',
position: 7,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.linkedinLink
],
personObjectMetadata.fields.find(
(field) =>
field.standardId === PERSON_STANDARD_FIELD_IDS.linkedinLink,
)?.id ?? '',
position: 8,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].fields[
PERSON_STANDARD_FIELD_IDS.xLink
],
personObjectMetadata.fields.find(
(field) => field.standardId === PERSON_STANDARD_FIELD_IDS.xLink,
)?.id ?? '',
position: 9,
isVisible: true,
size: 150,

View File

@ -1,18 +1,22 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
TASK_STANDARD_FIELD_IDS,
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const tasksAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
) => {
export const tasksAllView = (objectMetadataItems: ObjectMetadataEntity[]) => {
const taskObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.task,
);
if (!taskObjectMetadata) {
throw new Error('Task object metadata not found');
}
return {
name: 'All Tasks',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].id,
objectMetadataId: taskObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -32,72 +36,73 @@ export const tasksAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.title
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.title,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.taskTargets
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.taskTargets,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.createdBy
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.dueAt
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.dueAt,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.assignee
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.assignee,
)?.id ?? '',
position: 6,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.bodyV2
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.bodyV2,
)?.id ?? '',
position: 7,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
taskObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 8,
isVisible: true,
size: 150,

View File

@ -1,5 +1,4 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
TASK_STANDARD_FIELD_IDS,
@ -7,12 +6,19 @@ import {
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const tasksAssignedToMeView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const taskObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.task,
);
if (!taskObjectMetadata) {
throw new Error('Task object metadata not found');
}
return {
name: 'Assigned to Me',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].id,
objectMetadataId: taskObjectMetadata.id,
type: 'table',
key: null,
position: 2,
@ -21,9 +27,9 @@ export const tasksAssignedToMeView = (
filters: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.assignee
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.assignee,
)?.id ?? '',
displayValue: 'Me',
operand: 'is',
value: JSON.stringify({
@ -35,9 +41,9 @@ export const tasksAssignedToMeView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.title
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.title,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
@ -53,54 +59,55 @@ export const tasksAssignedToMeView = (
},*/
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.taskTargets
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.taskTargets,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.createdBy
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.dueAt
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.dueAt,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.assignee
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.assignee,
)?.id ?? '',
position: 6,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.body
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.body,
)?.id ?? '',
position: 7,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
taskObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 8,
isVisible: true,
size: 150,
@ -109,36 +116,36 @@ export const tasksAssignedToMeView = (
groups: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'TODO',
position: 0,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'IN_PROGRESS',
position: 1,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'DONE',
position: 2,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: '',
position: 3,

View File

@ -1,5 +1,4 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
TASK_STANDARD_FIELD_IDS,
@ -7,20 +6,27 @@ import {
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
export const tasksByStatusView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const taskObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.task,
);
if (!taskObjectMetadata) {
throw new Error('Task object metadata not found');
}
return {
name: 'By Status',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].id,
objectMetadataId: taskObjectMetadata.id,
type: 'kanban',
key: null,
position: 1,
icon: 'IconLayoutKanban',
kanbanFieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
filters: [] /* [
{
fieldMetadataId:
@ -35,45 +41,46 @@ export const tasksByStatusView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.title
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.title,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.dueAt
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.dueAt,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.assignee
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.assignee,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.createdAt
],
taskObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
)?.id ?? '',
position: 6,
isVisible: true,
size: 150,
@ -94,27 +101,27 @@ export const tasksByStatusView = (
groups: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'TODO',
position: 0,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'IN_PROGRESS',
position: 1,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.task].fields[
TASK_STANDARD_FIELD_IDS.status
],
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
isVisible: true,
fieldValue: 'DONE',
position: 2,

View File

@ -1,16 +1,22 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
export const workflowRunsAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const workflowRunObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.workflowRun,
);
if (!workflowRunObjectMetadata) {
throw new Error('Workflow run object metadata not found');
}
return {
name: 'All Workflow Runs',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun].id,
objectMetadataId: workflowRunObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -21,48 +27,61 @@ export const workflowRunsAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.name],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_RUN_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.workflow],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_RUN_STANDARD_FIELD_IDS.workflow,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.status],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_RUN_STANDARD_FIELD_IDS.status,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.startedAt],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_RUN_STANDARD_FIELD_IDS.startedAt,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.createdBy],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_RUN_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowRun]
.fields[WORKFLOW_RUN_STANDARD_FIELD_IDS.workflowVersion],
workflowRunObjectMetadata.fields.find(
(field) =>
field.standardId ===
WORKFLOW_RUN_STANDARD_FIELD_IDS.workflowVersion,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,

View File

@ -1,5 +1,4 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
WORKFLOW_VERSION_STANDARD_FIELD_IDS,
@ -8,12 +7,19 @@ import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
export const workflowVersionsAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const workflowVersionObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.workflowVersion,
);
if (!workflowVersionObjectMetadata) {
throw new Error('Workflow version object metadata not found');
}
return {
name: 'All Workflow Versions',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion].id,
objectMetadataId: workflowVersionObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -24,40 +30,50 @@ export const workflowVersionsAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion]
.fields[WORKFLOW_VERSION_STANDARD_FIELD_IDS.name],
workflowVersionObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_VERSION_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion]
.fields[WORKFLOW_VERSION_STANDARD_FIELD_IDS.workflow],
workflowVersionObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_VERSION_STANDARD_FIELD_IDS.workflow,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion]
.fields[WORKFLOW_VERSION_STANDARD_FIELD_IDS.status],
workflowVersionObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_VERSION_STANDARD_FIELD_IDS.status,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion]
.fields[BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt],
workflowVersionObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflowVersion]
.fields[WORKFLOW_VERSION_STANDARD_FIELD_IDS.runs],
workflowVersionObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_VERSION_STANDARD_FIELD_IDS.runs,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,

View File

@ -1,5 +1,4 @@
import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
BASE_OBJECT_STANDARD_FIELD_IDS,
WORKFLOW_STANDARD_FIELD_IDS,
@ -8,12 +7,19 @@ import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
export const workflowsAllView = (
objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap,
objectMetadataItems: ObjectMetadataEntity[],
) => {
const workflowObjectMetadata = objectMetadataItems.find(
(object) => object.standardId === STANDARD_OBJECT_IDS.workflow,
);
if (!workflowObjectMetadata) {
throw new Error('Workflow object metadata not found');
}
return {
name: 'All Workflows',
objectMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].id,
objectMetadataId: workflowObjectMetadata.id,
type: 'table',
key: 'INDEX',
position: 0,
@ -24,54 +30,58 @@ export const workflowsAllView = (
fields: [
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.name
],
workflowObjectMetadata.fields.find(
(field) => field.standardId === WORKFLOW_STANDARD_FIELD_IDS.name,
)?.id ?? '',
position: 0,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.statuses
],
workflowObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_STANDARD_FIELD_IDS.statuses,
)?.id ?? '',
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt
],
workflowObjectMetadata.fields.find(
(field) =>
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt,
)?.id ?? '',
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.createdBy
],
workflowObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_STANDARD_FIELD_IDS.createdBy,
)?.id ?? '',
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.versions
],
workflowObjectMetadata.fields.find(
(field) =>
field.standardId === WORKFLOW_STANDARD_FIELD_IDS.versions,
)?.id ?? '',
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId:
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.runs
],
workflowObjectMetadata.fields.find(
(field) => field.standardId === WORKFLOW_STANDARD_FIELD_IDS.runs,
)?.id ?? '',
position: 5,
isVisible: true,
size: 150,

View File

@ -2,7 +2,6 @@ import { WorkspaceTableStructure } from 'src/engine/workspace-manager/workspace-
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
export enum WorkspaceHealthIssueType {
MISSING_TABLE = 'MISSING_TABLE',
@ -88,17 +87,6 @@ export type WorkspaceRelationIssueTypes =
| WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT
| WorkspaceHealthIssueType.RELATION_TYPE_NOT_VALID;
export interface WorkspaceHealthRelationIssue<
T extends WorkspaceRelationIssueTypes,
> {
type: T;
fromFieldMetadata?: FieldMetadataEntity | undefined;
toFieldMetadata?: FieldMetadataEntity | undefined;
relationMetadata?: RelationMetadataEntity;
columnStructure?: WorkspaceTableStructure;
message: string;
}
/**
* Get the interface for the issue type
*/
@ -107,9 +95,7 @@ export type WorkspaceIssueTypeToInterface<T extends WorkspaceHealthIssueType> =
? WorkspaceHealthTableIssue<T>
: T extends WorkspaceColumnIssueTypes
? WorkspaceHealthColumnIssue<T>
: T extends WorkspaceRelationIssueTypes
? WorkspaceHealthRelationIssue<T>
: never;
: never;
/**
* Union of all issues

View File

@ -1,250 +0,0 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceTableStructure } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-table-definition.interface';
import {
WorkspaceHealthIssue,
WorkspaceHealthIssueType,
} from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-issue.interface';
import {
WorkspaceHealthMode,
WorkspaceHealthOptions,
} from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-options.interface';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
RelationMetadataEntity,
RelationMetadataType,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import {
RelationDirection,
deduceRelationDirection,
} from 'src/engine/utils/deduce-relation-direction.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { createRelationForeignKeyFieldMetadataName } from 'src/engine/metadata-modules/relation-metadata/utils/create-relation-foreign-key-field-metadata-name.util';
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
import { convertOnDeleteActionToOnDelete } from 'src/engine/workspace-manager/workspace-migration-runner/utils/convert-on-delete-action-to-on-delete.util';
import { camelCase } from 'src/utils/camel-case';
@Injectable()
export class RelationMetadataHealthService {
constructor() {}
public healthCheck(
workspaceTableColumns: WorkspaceTableStructure[],
objectMetadataCollection: ObjectMetadataEntity[],
objectMetadata: ObjectMetadataEntity,
options: WorkspaceHealthOptions,
) {
const issues: WorkspaceHealthIssue[] = [];
for (const fieldMetadata of objectMetadata.fields) {
// We're only interested in relation fields
if (!isRelationFieldMetadataType(fieldMetadata.type)) {
continue;
}
const relationMetadata =
fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata;
if (!relationMetadata) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_METADATA_NOT_VALID,
message: `Field ${fieldMetadata.id} has invalid relation metadata`,
});
continue;
}
const relationDirection = deduceRelationDirection(
fieldMetadata,
relationMetadata,
);
// Many to many relations are not supported yet
if (relationMetadata.relationType === RelationMetadataType.MANY_TO_MANY) {
return [];
}
const fromObjectMetadata = objectMetadataCollection.find(
(objectMetadata) =>
objectMetadata.id === relationMetadata.fromObjectMetadataId,
);
const fromFieldMetadata = fromObjectMetadata?.fields.find(
(fieldMetadata) =>
fieldMetadata.id === relationMetadata.fromFieldMetadataId,
);
const toObjectMetadata = objectMetadataCollection.find(
(objectMetadata) =>
objectMetadata.id === relationMetadata.toObjectMetadataId,
);
const toFieldMetadata = toObjectMetadata?.fields.find(
(fieldMetadata) =>
fieldMetadata.id === relationMetadata.toFieldMetadataId,
);
if (!fromFieldMetadata || !toFieldMetadata) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FROM_OR_TO_FIELD_METADATA_NOT_VALID,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${relationMetadata.id} has invalid from or to field metadata`,
});
return issues;
}
if (
options.mode === WorkspaceHealthMode.All ||
options.mode === WorkspaceHealthMode.Structure
) {
// Check relation structure
const structureIssues = this.structureRelationCheck(
fromFieldMetadata,
toFieldMetadata,
toObjectMetadata?.fields ?? [],
relationDirection,
relationMetadata,
workspaceTableColumns,
);
issues.push(...structureIssues);
}
if (
options.mode === WorkspaceHealthMode.All ||
options.mode === WorkspaceHealthMode.Metadata
) {
// Check relation metadata
const metadataIssues = this.metadataRelationCheck(
fromFieldMetadata,
toFieldMetadata,
relationDirection,
relationMetadata,
);
issues.push(...metadataIssues);
}
}
return issues;
}
private structureRelationCheck(
fromFieldMetadata: FieldMetadataEntity,
toFieldMetadata: FieldMetadataEntity,
toObjectMetadataFields: FieldMetadataEntity[],
relationDirection: RelationDirection,
relationMetadata: RelationMetadataEntity,
workspaceTableColumns: WorkspaceTableStructure[],
): WorkspaceHealthIssue[] {
const issues: WorkspaceHealthIssue[] = [];
// Nothing to check on the structure
if (relationDirection === RelationDirection.FROM) {
return [];
}
const foreignKeyColumnName = `${camelCase(toFieldMetadata.name)}Id`;
const relationColumn = workspaceTableColumns.find(
(column) => column.columnName === foreignKeyColumnName,
);
const relationFieldMetadata = toObjectMetadataFields.find(
(fieldMetadata) =>
fieldMetadata.name ===
createRelationForeignKeyFieldMetadataName(toFieldMetadata.name),
);
if (!relationFieldMetadata) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${
relationMetadata.id
} doesn't have a valid foreign key (expected fieldMetadata.name to be ${createRelationForeignKeyFieldMetadataName(
toFieldMetadata.name,
)}`,
});
return issues;
}
if (!relationColumn) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${relationMetadata.id} doesn't have a valid foreign key (expected column name to be ${foreignKeyColumnName}`,
});
return issues;
}
if (!relationColumn.isForeignKey) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_CONFLICT,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${relationMetadata.id} foreign key is not properly set`,
});
}
if (
relationMetadata.relationType === RelationMetadataType.ONE_TO_ONE &&
!relationColumn.isUnique
) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_CONFLICT,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${relationMetadata.id} foreign key is not marked as unique and relation type is one-to-one`,
});
}
if (
convertOnDeleteActionToOnDelete(relationMetadata.onDeleteAction) !==
relationColumn.onDeleteAction
) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
columnStructure: relationColumn,
message: `Relation ${relationMetadata.id} foreign key onDeleteAction is not properly set`,
});
}
return issues;
}
private metadataRelationCheck(
fromFieldMetadata: FieldMetadataEntity,
toFieldMetadata: FieldMetadataEntity,
relationDirection: RelationDirection,
relationMetadata: RelationMetadataEntity,
): WorkspaceHealthIssue[] {
const issues: WorkspaceHealthIssue[] = [];
if (
!Object.values(RelationMetadataType).includes(
relationMetadata.relationType,
)
) {
issues.push({
type: WorkspaceHealthIssueType.RELATION_TYPE_NOT_VALID,
fromFieldMetadata,
toFieldMetadata,
relationMetadata,
message: `Relation ${relationMetadata.id} has invalid relation type`,
});
}
return issues;
}
}

View File

@ -7,7 +7,6 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service';
import { FieldMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/field-metadata-health.service';
import { ObjectMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/object-metadata-health.service';
import { RelationMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/relation-metadata.health.service';
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
@ -31,7 +30,6 @@ import { WorkspaceFixService } from './services/workspace-fix.service';
DatabaseStructureService,
ObjectMetadataHealthService,
FieldMetadataHealthService,
RelationMetadataHealthService,
WorkspaceFixService,
],
exports: [WorkspaceHealthService],

View File

@ -18,7 +18,6 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service';
import { FieldMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/field-metadata-health.service';
import { ObjectMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/object-metadata-health.service';
import { RelationMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/relation-metadata.health.service';
import { WorkspaceFixService } from 'src/engine/workspace-manager/workspace-health/services/workspace-fix.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
@ -27,15 +26,14 @@ export class WorkspaceHealthService {
private readonly logger = new Logger(WorkspaceHealthService.name);
constructor(
@InjectDataSource('metadata')
private readonly metadataDataSource: DataSource,
@InjectDataSource('core')
private readonly coreDataSource: DataSource,
private readonly dataSourceService: DataSourceService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly databaseStructureService: DatabaseStructureService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly objectMetadataHealthService: ObjectMetadataHealthService,
private readonly fieldMetadataHealthService: FieldMetadataHealthService,
private readonly relationMetadataHealthService: RelationMetadataHealthService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceFixService: WorkspaceFixService,
) {}
@ -100,16 +98,6 @@ export class WorkspaceHealthService {
);
issues.push(...fieldIssues);
// Check relation metadata health
const relationIssues = this.relationMetadataHealthService.healthCheck(
workspaceTableColumns,
objectMetadataCollection,
objectMetadata,
options,
);
issues.push(...relationIssues);
}
return issues;
@ -132,7 +120,7 @@ export class WorkspaceHealthService {
// Set default options
options.applyChanges ??= true;
const queryRunner = this.metadataDataSource.createQueryRunner();
const queryRunner = this.coreDataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

View File

@ -8,14 +8,13 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
import { SeederModule } from 'src/engine/seeder/seeder.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { DevSeederModule } from 'src/engine/workspace-manager/dev-seeder/dev-seeder.module';
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
@ -26,7 +25,7 @@ import { WorkspaceManagerService } from './workspace-manager.service';
WorkspaceDataSourceModule,
WorkspaceMigrationModule,
ObjectMetadataModule,
SeederModule,
DevSeederModule,
DataSourceModule,
WorkspaceSyncMetadataModule,
WorkspaceHealthModule,
@ -36,13 +35,8 @@ import { WorkspaceManagerService } from './workspace-manager.service';
RoleModule,
UserRoleModule,
TypeOrmModule.forFeature(
[
FieldMetadataEntity,
RelationMetadataEntity,
UserWorkspaceRoleEntity,
RoleEntity,
],
'metadata',
[FieldMetadataEntity, UserWorkspaceRoleEntity, RoleEntity],
'core',
),
],
exports: [WorkspaceManagerService],

View File

@ -1,14 +1,8 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { Repository } from 'typeorm';
import { DEV_SEED_USER_WORKSPACE_IDS } from 'src/database/typeorm-seeds/core/user-workspaces';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/database/typeorm-seeds/core/workspaces';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@ -16,20 +10,12 @@ import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-s
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { PETS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/pets-data-seeds';
import { SURVEY_RESULTS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/survey-results-data-seeds';
import { PETS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/pets-metadata-seeds';
import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds';
import { SeederService } from 'src/engine/seeder/seeder.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { seedWorkspaceWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-with-demo-data';
import { standardObjectsPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@ -41,14 +27,10 @@ export class WorkspaceManagerService {
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly workspaceMigrationService: WorkspaceMigrationService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly seederService: SeederService,
private readonly dataSourceService: DataSourceService,
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
private readonly permissionsService: PermissionsService,
@InjectRepository(FieldMetadataEntity, 'metadata')
@InjectRepository(FieldMetadataEntity, 'core')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
@InjectRepository(RelationMetadataEntity, 'metadata')
private readonly relationMetadataRepository: Repository<RelationMetadataEntity>,
@InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
private readonly roleService: RoleService,
@ -56,17 +38,12 @@ export class WorkspaceManagerService {
private readonly featureFlagService: FeatureFlagService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(UserWorkspaceRoleEntity, 'metadata')
@InjectRepository(UserWorkspaceRoleEntity, 'core')
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
@InjectRepository(RoleEntity, 'metadata')
@InjectRepository(RoleEntity, 'core')
private readonly roleRepository: Repository<RoleEntity>,
) {}
/**
* Init a workspace by creating a new data source and running all migrations
* @param workspaceId
* @returns Promise<void>
*/
public async init({
workspaceId,
userId,
@ -120,7 +97,7 @@ export class WorkspaceManagerService {
const prefillStandardObjectsStart = performance.now();
await this.prefillWorkspaceWithStandardObjects(
await this.prefillWorkspaceWithStandardObjectsRecords(
dataSourceMetadata,
workspaceId,
);
@ -132,67 +109,7 @@ export class WorkspaceManagerService {
);
}
/**
* InitDemo a workspace by creating a new data source and running all migrations
* @param workspaceId
* @returns Promise<void>
*/
public async initDemo(workspaceId: string): Promise<void> {
const schemaName =
await this.workspaceDataSourceService.createWorkspaceDBSchema(
workspaceId,
);
const dataSourceMetadata =
await this.dataSourceService.createDataSourceMetadata(
workspaceId,
schemaName,
);
const featureFlags =
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
await this.workspaceSyncMetadataService.synchronize({
workspaceId,
dataSourceId: dataSourceMetadata.id,
featureFlags,
});
await this.prefillWorkspaceWithDemoObjects(dataSourceMetadata, workspaceId);
}
public async initDev(workspaceId: string): Promise<void> {
const schemaName =
await this.workspaceDataSourceService.createWorkspaceDBSchema(
workspaceId,
);
const dataSourceMetadata =
await this.dataSourceService.createDataSourceMetadata(
workspaceId,
schemaName,
);
const featureFlags =
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
await this.workspaceSyncMetadataService.synchronize({
workspaceId: workspaceId,
dataSourceId: dataSourceMetadata.id,
featureFlags,
});
await this.initPermissionsDev(workspaceId);
}
/**
*
* We are prefilling a few standard objects with data to make it easier for the user to get started.
*
* @param dataSourceMetadata
* @param workspaceId
*/
private async prefillWorkspaceWithStandardObjects(
private async prefillWorkspaceWithStandardObjectsRecords(
dataSourceMetadata: DataSourceEntity,
workspaceId: string,
) {
@ -213,74 +130,10 @@ export class WorkspaceManagerService {
);
}
/**
*
* We are prefilling a few demo objects with data to make it easier for the user to get started.
*
* @param dataSourceMetadata
* @param workspaceId
*/
private async prefillWorkspaceWithDemoObjects(
dataSourceMetadata: DataSourceEntity,
workspaceId: string,
) {
const mainDataSource =
await this.workspaceDataSourceService.connectToMainDataSource();
if (!mainDataSource) {
throw new Error('Could not connect to main data source');
}
const createdObjectMetadata =
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
await seedWorkspaceWithDemoData(
mainDataSource,
dataSourceMetadata.schema,
createdObjectMetadata,
);
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
workspaceId,
PETS_METADATA_SEEDS,
);
await this.seederService.seedCustomObjectRecords(
workspaceId,
PETS_METADATA_SEEDS,
PETS_DATA_SEEDS,
);
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
workspaceId,
SURVEY_RESULTS_METADATA_SEEDS,
);
await this.seederService.seedCustomObjectRecords(
workspaceId,
SURVEY_RESULTS_METADATA_SEEDS,
SURVEY_RESULTS_DATA_SEEDS,
);
}
/**
*
* Delete a workspace by deleting all metadata and the schema
*
* @param workspaceId
*/
public async delete(workspaceId: string): Promise<void> {
//TODO: delete all logs when #611 closed
this.logger.log(`Deleting workspace ${workspaceId} ...`);
// Delete data from metadata tables
await this.relationMetadataRepository.delete({
workspaceId,
});
this.logger.log(`workspace ${workspaceId} relation metadata deleted`);
await this.fieldMetadataRepository.delete({
workspaceId,
});
@ -341,56 +194,4 @@ export class WorkspaceManagerService {
defaultRoleId: memberRole.id,
});
}
private async initPermissionsDev(workspaceId: string) {
const adminRole = await this.roleService.createAdminRole({
workspaceId,
});
let adminUserWorkspaceId: string | undefined;
let memberUserWorkspaceId: string | undefined;
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
adminUserWorkspaceId = DEV_SEED_USER_WORKSPACE_IDS.TIM;
memberUserWorkspaceId = DEV_SEED_USER_WORKSPACE_IDS.JONY;
// Create guest role only in this workspace
const guestRole = await this.roleService.createGuestRole({
workspaceId,
});
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: DEV_SEED_USER_WORKSPACE_IDS.PHIL,
roleId: guestRole.id,
});
} else if (workspaceId === SEED_ACME_WORKSPACE_ID) {
adminUserWorkspaceId = DEV_SEED_USER_WORKSPACE_IDS.TIM_ACME;
}
if (adminUserWorkspaceId) {
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: adminUserWorkspaceId,
roleId: adminRole.id,
});
}
const memberRole = await this.roleService.createMemberRole({
workspaceId,
});
await this.workspaceRepository.update(workspaceId, {
defaultRoleId: memberRole.id,
activationStatus: WorkspaceActivationStatus.ACTIVE,
});
if (memberUserWorkspaceId) {
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: memberUserWorkspaceId,
roleId: memberRole.id,
});
}
}
}

View File

@ -3,12 +3,10 @@ import { WorkspaceMigrationIndexFactory } from 'src/engine/workspace-manager/wor
import { WorkspaceMigrationFieldRelationFactory } from './workspace-migration-field-relation.factory';
import { WorkspaceMigrationFieldFactory } from './workspace-migration-field.factory';
import { WorkspaceMigrationObjectFactory } from './workspace-migration-object.factory';
import { WorkspaceMigrationRelationFactory } from './workspace-migration-relation.factory';
export const workspaceMigrationBuilderFactories = [
WorkspaceMigrationObjectFactory,
WorkspaceMigrationFieldFactory,
WorkspaceMigrationFieldRelationFactory,
WorkspaceMigrationRelationFactory,
WorkspaceMigrationIndexFactory,
];

View File

@ -5,7 +5,6 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import {
WorkspaceMigrationColumnActionType,
@ -40,10 +39,6 @@ export class WorkspaceMigrationObjectFactory {
async create(
objectMetadataCollection: ObjectMetadataEntity[],
action: WorkspaceMigrationBuilderAction.DELETE,
relationMetadataByFromObjectMetadataId: Record<
string,
RelationMetadataEntity[]
>,
): Promise<Partial<WorkspaceMigrationEntity>[]>;
async create(
@ -51,10 +46,6 @@ export class WorkspaceMigrationObjectFactory {
| ObjectMetadataEntity[]
| ObjectMetadataUpdate[],
action: WorkspaceMigrationBuilderAction,
relationMetadataByFromObjectMetadataId?: Record<
string,
RelationMetadataEntity[]
>,
): Promise<Partial<WorkspaceMigrationEntity>[]> {
switch (action) {
case WorkspaceMigrationBuilderAction.CREATE:
@ -68,10 +59,6 @@ export class WorkspaceMigrationObjectFactory {
case WorkspaceMigrationBuilderAction.DELETE:
return this.deleteObjectMigration(
objectMetadataCollectionOrObjectMetadataUpdateCollection as ObjectMetadataEntity[],
relationMetadataByFromObjectMetadataId as Record<
string,
RelationMetadataEntity[]
>,
);
default:
return [];
@ -153,37 +140,15 @@ export class WorkspaceMigrationObjectFactory {
private async deleteObjectMigration(
objectMetadataCollection: ObjectMetadataEntity[],
relationMetadataByFromObjectMetadataId: Record<
string,
RelationMetadataEntity[]
>,
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const objectMetadata of objectMetadataCollection) {
const relationMetadataCollection =
relationMetadataByFromObjectMetadataId[objectMetadata.id];
workspaceMigrations.push({
workspaceId: objectMetadata.workspaceId,
name: generateMigrationName(`delete-${objectMetadata.nameSingular}`),
isCustom: objectMetadata.isCustom,
migrations: [
...(relationMetadataCollection ?? []).map(
(relationMetadata) =>
({
name: computeObjectTargetTable(
relationMetadata.toObjectMetadata,
),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY,
columnName: `${relationMetadata.toFieldMetadata.name}Id`,
},
],
}) satisfies WorkspaceMigrationTableAction,
),
{
name: computeObjectTargetTable(objectMetadata),
action: WorkspaceMigrationTableActionType.DROP,

View File

@ -1,198 +0,0 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import {
RelationMetadataEntity,
RelationMetadataType,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { camelCase } from 'src/utils/camel-case';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
@Injectable()
export class WorkspaceMigrationRelationFactory {
constructor() {}
/**
* Deletion of the relation is handled by field deletion
*/
async create(
originalObjectMetadataCollection: ObjectMetadataEntity[],
relationMetadataCollection: RelationMetadataEntity[],
action: WorkspaceMigrationBuilderAction,
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const originalObjectMetadataMap = originalObjectMetadataCollection.reduce(
(result, currentObject) => {
result[currentObject.id] = currentObject;
return result;
},
{} as Record<string, ObjectMetadataEntity>,
);
switch (action) {
case WorkspaceMigrationBuilderAction.CREATE:
return this.createRelationMigration(
originalObjectMetadataMap,
relationMetadataCollection,
);
case WorkspaceMigrationBuilderAction.UPDATE:
return this.updateRelationMigration(
originalObjectMetadataMap,
relationMetadataCollection,
);
default:
return [];
}
}
private async updateRelationMigration(
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
relationMetadataCollection: RelationMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const relationMetadata of relationMetadataCollection) {
const toObjectMetadata =
originalObjectMetadataMap[relationMetadata.toObjectMetadataId];
const fromObjectMetadata =
originalObjectMetadataMap[relationMetadata.fromObjectMetadataId];
if (!toObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.toObjectMetadataId} not found`,
);
}
if (!fromObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.fromObjectMetadataId} not found`,
);
}
const toFieldMetadata = toObjectMetadata.fields.find(
(field) => field.id === relationMetadata.toFieldMetadataId,
);
if (!toFieldMetadata) {
throw new Error(
`FieldMetadata with id ${relationMetadata.toFieldMetadataId} not found`,
);
}
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(toObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY,
columnName: `${camelCase(toFieldMetadata.name)}Id`,
},
],
},
{
name: computeObjectTargetTable(toObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
columnName: `${camelCase(toFieldMetadata.name)}Id`,
referencedTableName: computeObjectTargetTable(fromObjectMetadata),
referencedTableColumnName: 'id',
isUnique:
relationMetadata.relationType ===
RelationMetadataType.ONE_TO_ONE,
onDelete: relationMetadata.onDeleteAction,
},
],
},
];
workspaceMigrations.push({
workspaceId: relationMetadata.workspaceId,
name: generateMigrationName(
`update-relation-from-${fromObjectMetadata.nameSingular}-to-${toObjectMetadata.nameSingular}`,
),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
private async createRelationMigration(
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
relationMetadataCollection: RelationMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const relationMetadata of relationMetadataCollection) {
const toObjectMetadata =
originalObjectMetadataMap[relationMetadata.toObjectMetadataId];
const fromObjectMetadata =
originalObjectMetadataMap[relationMetadata.fromObjectMetadataId];
if (!toObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.toObjectMetadataId} not found`,
);
}
if (!fromObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.fromObjectMetadataId} not found`,
);
}
const toFieldMetadata = toObjectMetadata.fields.find(
(field) => field.id === relationMetadata.toFieldMetadataId,
);
if (!toFieldMetadata) {
throw new Error(
`FieldMetadata with id ${relationMetadata.toFieldMetadataId} not found`,
);
}
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(toObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
columnName: `${camelCase(toFieldMetadata.name)}Id`,
referencedTableName: computeObjectTargetTable(fromObjectMetadata),
referencedTableColumnName: 'id',
isUnique:
relationMetadata.relationType ===
RelationMetadataType.ONE_TO_ONE,
onDelete: relationMetadata.onDeleteAction,
},
],
},
];
workspaceMigrations.push({
workspaceId: relationMetadata.workspaceId,
name: generateMigrationName(
`create-relation-from-${fromObjectMetadata.nameSingular}-to-${toObjectMetadata.nameSingular}`,
),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
}

View File

@ -1,4 +1,4 @@
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
export const convertOnDeleteActionToOnDelete = (
onDeleteAction: RelationOnDeleteAction | undefined,

View File

@ -61,23 +61,5 @@ export class SyncWorkspaceLoggerService {
`${workspaceId}/field-metadata-delete-collection`,
storage.fieldMetadataDeleteCollection,
);
// Save relation metadata create collection
await this.commandLogger.writeLog(
`${workspaceId}/relation-metadata-create-collection`,
storage.relationMetadataCreateCollection,
);
// Save relation metadata update collection
await this.commandLogger.writeLog(
`${workspaceId}/relation-metadata-update-collection`,
storage.relationMetadataUpdateCollection,
);
// Save relation metadata delete collection
await this.commandLogger.writeLog(
`${workspaceId}/relation-metadata-delete-collection`,
storage.relationMetadataDeleteCollection,
);
}
}

View File

@ -1,86 +0,0 @@
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
import { WorkspaceRelationComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-relation.comparator';
describe('WorkspaceRelationComparator', () => {
let comparator: WorkspaceRelationComparator;
beforeEach(() => {
comparator = new WorkspaceRelationComparator();
});
function createMockRelationMetadata(values: any) {
return {
fromObjectMetadataId: 'object-1',
fromFieldMetadataId: 'field-1',
...values,
};
}
it('should generate CREATE action for new relations', () => {
// @ts-expect-error legacy noImplicitAny
const original = [];
const standard = [createMockRelationMetadata({})];
// @ts-expect-error legacy noImplicitAny
const result = comparator.compare(original, standard);
expect(result).toEqual([
{
action: ComparatorAction.CREATE,
object: expect.objectContaining({
fromObjectMetadataId: 'object-1',
fromFieldMetadataId: 'field-1',
}),
},
]);
});
it('should generate DELETE action for removed relations', () => {
const original = [createMockRelationMetadata({ id: '1' })];
// @ts-expect-error legacy noImplicitAny
const standard = [];
// @ts-expect-error legacy noImplicitAny
const result = comparator.compare(original, standard);
expect(result).toEqual([
{
action: ComparatorAction.DELETE,
object: expect.objectContaining({ id: '1' }),
},
]);
});
it('should generate UPDATE action for changed relations', () => {
const original = [
createMockRelationMetadata({ onDeleteAction: 'CASCADE' }),
];
const standard = [
createMockRelationMetadata({ onDeleteAction: 'SET_NULL' }),
];
const result = comparator.compare(original, standard);
expect(result).toEqual([
{
action: ComparatorAction.UPDATE,
object: expect.objectContaining({
fromObjectMetadataId: 'object-1',
fromFieldMetadataId: 'field-1',
onDeleteAction: 'SET_NULL',
}),
},
]);
});
it('should not generate any action for identical relations', () => {
const relation = createMockRelationMetadata({});
const original = [{ id: '1', ...relation }];
const standard = [relation];
const result = comparator.compare(original, standard);
expect(result).toHaveLength(0);
});
});

View File

@ -3,12 +3,10 @@ import { WorkspaceIndexComparator } from 'src/engine/workspace-manager/workspace
import { WorkspaceFieldComparator } from './workspace-field.comparator';
import { WorkspaceObjectComparator } from './workspace-object.comparator';
import { WorkspaceRelationComparator } from './workspace-relation.comparator';
export const workspaceSyncMetadataComparators = [
WorkspaceFieldComparator,
WorkspaceFieldRelationComparator,
WorkspaceObjectComparator,
WorkspaceRelationComparator,
WorkspaceIndexComparator,
];

View File

@ -1,112 +0,0 @@
import { Injectable } from '@nestjs/common';
import diff from 'microdiff';
import {
ComparatorAction,
RelationComparatorResult,
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
const relationPropertiesToIgnore = ['createdAt', 'updatedAt'];
const relationPropertiesToUpdate = ['onDeleteAction'];
@Injectable()
export class WorkspaceRelationComparator {
constructor() {}
compare(
originalRelationMetadataCollection: RelationMetadataEntity[],
standardRelationMetadataCollection: Partial<RelationMetadataEntity>[],
): RelationComparatorResult[] {
const results: RelationComparatorResult[] = [];
// Create a map of standard relations
const standardRelationMetadataMap = transformMetadataForComparison(
standardRelationMetadataCollection,
{
keyFactory(relationMetadata) {
return `${relationMetadata.fromObjectMetadataId}->${relationMetadata.fromFieldMetadataId}`;
},
},
);
// Create a filtered map of original relations
// We filter out 'id' later because we need it to remove the relation from DB
const originalRelationMetadataMap = transformMetadataForComparison(
originalRelationMetadataCollection,
{
shouldIgnoreProperty: (property) =>
relationPropertiesToIgnore.includes(property),
keyFactory(relationMetadata) {
return `${relationMetadata.fromObjectMetadataId}->${relationMetadata.fromFieldMetadataId}`;
},
},
);
// Compare relations
const relationMetadataDifference = diff(
originalRelationMetadataMap,
standardRelationMetadataMap,
);
for (const difference of relationMetadataDifference) {
switch (difference.type) {
case 'CREATE': {
results.push({
action: ComparatorAction.CREATE,
object: difference.value,
});
break;
}
case 'REMOVE': {
if (difference.path[difference.path.length - 1] !== 'id') {
results.push({
action: ComparatorAction.DELETE,
object: difference.oldValue,
});
}
break;
}
case 'CHANGE': {
const fieldName = difference.path[0];
const property = difference.path[difference.path.length - 1];
if (!relationPropertiesToUpdate.includes(property as string)) {
continue;
}
const originalRelationMetadata =
originalRelationMetadataMap[fieldName];
if (!originalRelationMetadata) {
throw new Error(
`Relation ${fieldName} not found in originalRelationMetadataMap`,
);
}
results.push({
action: ComparatorAction.UPDATE,
object: {
id: originalRelationMetadata.id,
fromObjectMetadataId:
originalRelationMetadata.fromObjectMetadataId,
fromFieldMetadataId: originalRelationMetadata.fromFieldMetadataId,
toObjectMetadataId: originalRelationMetadata.toObjectMetadataId,
toFieldMetadataId: originalRelationMetadata.toFieldMetadataId,
workspaceId: originalRelationMetadata.workspaceId,
...{
[property]: difference.value,
},
},
});
break;
}
}
}
return results;
}
}

View File

@ -3,12 +3,10 @@ import { StandardIndexFactory } from 'src/engine/workspace-manager/workspace-syn
import { StandardFieldRelationFactory } from './standard-field-relation.factory';
import { StandardFieldFactory } from './standard-field.factory';
import { StandardObjectFactory } from './standard-object.factory';
import { StandardRelationFactory } from './standard-relation.factory';
export const workspaceSyncMetadataFactories = [
StandardFieldFactory,
StandardObjectFactory,
StandardRelationFactory,
StandardFieldRelationFactory,
StandardIndexFactory,
];

View File

@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
import { FieldMetadataType } from 'twenty-shared/types';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { WorkspaceDynamicRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-dynamic-relation-metadata-args.interface';
import { WorkspaceEntityMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-entity-metadata-args.interface';
import { WorkspaceFieldMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface';
@ -190,7 +189,7 @@ export class StandardFieldFactory {
workspaceEntityMetadataArgs?.isSystem ||
workspaceRelationMetadataArgs.isSystem,
isNullable: true,
isUnique: workspaceRelationMetadataArgs.type === RelationType.ONE_TO_ONE,
isUnique: false,
isActive: workspaceRelationMetadataArgs.isActive ?? true,
});

View File

@ -1,170 +0,0 @@
import { Injectable } from '@nestjs/common';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
RelationMetadataEntity,
RelationMetadataType,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
import { assert } from 'src/utils/assert';
interface CustomRelationFactory {
object: ObjectMetadataEntity;
metadata: typeof BaseWorkspaceEntity;
}
@Injectable()
export class StandardRelationFactory {
create(
customObjectFactories: CustomRelationFactory[],
context: WorkspaceSyncContext,
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
): Partial<RelationMetadataEntity>[];
create(
standardObjectMetadataDefinitions: (typeof BaseWorkspaceEntity)[],
context: WorkspaceSyncContext,
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
): Partial<RelationMetadataEntity>[];
create(
standardObjectMetadataDefinitionsOrCustomObjectFactories:
| (typeof BaseWorkspaceEntity)[]
| {
object: ObjectMetadataEntity;
metadata: typeof BaseWorkspaceEntity;
}[],
context: WorkspaceSyncContext,
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
): Partial<RelationMetadataEntity>[] {
return standardObjectMetadataDefinitionsOrCustomObjectFactories.flatMap(
(
standardObjectMetadata:
| typeof BaseWorkspaceEntity
| CustomRelationFactory,
) =>
this.createRelationMetadata(
standardObjectMetadata,
context,
originalObjectMetadataMap,
),
);
}
private createRelationMetadata(
workspaceEntityOrCustomRelationFactory:
| typeof BaseWorkspaceEntity
| CustomRelationFactory,
context: WorkspaceSyncContext,
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
): Partial<RelationMetadataEntity>[] {
const target =
'metadata' in workspaceEntityOrCustomRelationFactory
? workspaceEntityOrCustomRelationFactory.metadata
: workspaceEntityOrCustomRelationFactory;
const workspaceEntity =
'metadata' in workspaceEntityOrCustomRelationFactory
? metadataArgsStorage.filterExtendedEntities(target)
: metadataArgsStorage.filterEntities(target);
const workspaceRelationMetadataArgsCollection =
metadataArgsStorage.filterRelations(target);
if (!workspaceEntity) {
throw new Error(
`Object metadata decorator not found, can't parse ${target.name}`,
);
}
if (
!workspaceRelationMetadataArgsCollection ||
isGatedAndNotEnabled(workspaceEntity?.gate, context.featureFlags)
) {
return [];
}
return workspaceRelationMetadataArgsCollection
.filter((workspaceRelationMetadataArgs) => {
// We're not storing many-to-one relations in the DB for the moment
if (workspaceRelationMetadataArgs.type === RelationType.MANY_TO_ONE) {
return false;
}
return !isGatedAndNotEnabled(
workspaceRelationMetadataArgs.gate,
context.featureFlags,
);
})
.map((workspaceRelationMetadataArgs) => {
// Compute reflect relation metadata
const fromObjectNameSingular =
'object' in workspaceEntityOrCustomRelationFactory
? workspaceEntityOrCustomRelationFactory.object.nameSingular
: convertClassNameToObjectMetadataName(
workspaceRelationMetadataArgs.target.name,
);
const toObjectNameSingular = convertClassNameToObjectMetadataName(
workspaceRelationMetadataArgs.inverseSideTarget().name,
);
const fromFieldMetadataName = workspaceRelationMetadataArgs.name;
const toFieldMetadataName =
(workspaceRelationMetadataArgs.inverseSideFieldKey as
| string
| undefined) ?? fromObjectNameSingular;
const fromObjectMetadata =
originalObjectMetadataMap[fromObjectNameSingular];
assert(
fromObjectMetadata,
`Object ${fromObjectNameSingular} not found in DB
for relation FROM defined in class ${fromObjectNameSingular}`,
);
const toObjectMetadata =
originalObjectMetadataMap[toObjectNameSingular];
assert(
toObjectMetadata,
`Object ${toObjectNameSingular} not found in DB
for relation TO defined in class ${fromObjectNameSingular}`,
);
const fromFieldMetadata = fromObjectMetadata?.fields.find(
(field) => field.name === fromFieldMetadataName,
);
assert(
fromFieldMetadata,
`Field ${fromFieldMetadataName} not found in object ${fromObjectNameSingular}
for relation FROM defined in class ${fromObjectNameSingular}`,
);
const toFieldMetadata = toObjectMetadata?.fields.find(
(field) => field.name === toFieldMetadataName,
);
assert(
toFieldMetadata,
`Field ${toFieldMetadataName} not found in object ${toObjectNameSingular}
for relation TO defined in class ${fromObjectNameSingular}`,
);
return {
// TODO: Will be removed when we drop RelationMetadata
relationType:
workspaceRelationMetadataArgs.type as unknown as RelationMetadataType,
fromObjectMetadataId: fromObjectMetadata?.id,
toObjectMetadataId: toObjectMetadata?.id,
fromFieldMetadataId: fromFieldMetadata?.id,
toFieldMetadataId: toFieldMetadata?.id,
workspaceId: context.workspaceId,
onDeleteAction: workspaceRelationMetadataArgs.onDelete,
};
});
}
}

View File

@ -2,7 +2,6 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { ComputedPartialFieldMetadata } from './partial-field-metadata.interface';
import { ComputedPartialWorkspaceEntity } from './partial-object-metadata.interface';
@ -68,11 +67,6 @@ export type FieldRelationComparatorResult =
>
| ComparatorDeleteResult<FieldMetadataEntity<FieldMetadataType.RELATION>>;
export type RelationComparatorResult =
| ComparatorCreateResult<Partial<RelationMetadataEntity>>
| ComparatorDeleteResult<RelationMetadataEntity>
| ComparatorUpdateResult<Partial<RelationMetadataEntity>>;
export type IndexComparatorResult =
| ComparatorCreateResult<Partial<IndexMetadataEntity>>
| ComparatorUpdateResult<Partial<IndexMetadataEntity>>

View File

@ -7,7 +7,7 @@ import {
export type PartialWorkspaceEntity = Omit<
ObjectMetadataInterface,
'id' | 'standardId' | 'fromRelations' | 'toRelations' | 'fields' | 'isActive'
'id' | 'standardId' | 'fields' | 'isActive'
> & {
standardId: string;
icon?: string;

View File

@ -25,7 +25,6 @@ import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
import { FieldMetadataUpdate } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field.factory';
@ -300,66 +299,6 @@ export class WorkspaceMetadataUpdaterService {
}
}
async updateRelationMetadata(
manager: EntityManager,
storage: WorkspaceSyncStorage,
): Promise<{
createdRelationMetadataCollection: RelationMetadataEntity[];
updatedRelationMetadataCollection: RelationMetadataEntity[];
}> {
const relationMetadataRepository = manager.getRepository(
RelationMetadataEntity,
);
const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity);
/**
* Create relation metadata
*/
const createdRelationMetadataCollection =
await relationMetadataRepository.save(
storage.relationMetadataCreateCollection,
);
/**
* Update relation metadata
*/
const updatedRelationMetadataCollection =
await relationMetadataRepository.save(
storage.relationMetadataUpdateCollection,
);
/**
* Delete relation metadata
*/
if (storage.relationMetadataDeleteCollection.length > 0) {
await relationMetadataRepository.delete(
storage.relationMetadataDeleteCollection.map(
(relationMetadata) => relationMetadata.id,
),
);
}
/**
* Delete related field metadata
*/
const fieldMetadataDeleteCollectionOnlyRelation =
storage.fieldMetadataDeleteCollection.filter(
(field) => field.type === FieldMetadataType.RELATION,
);
if (fieldMetadataDeleteCollectionOnlyRelation.length > 0) {
await fieldMetadataRepository.delete(
fieldMetadataDeleteCollectionOnlyRelation.map((field) => field.id),
);
}
return {
createdRelationMetadataCollection,
updatedRelationMetadataCollection,
};
}
async updateIndexMetadata(
manager: EntityManager,
storage: WorkspaceSyncStorage,

View File

@ -7,7 +7,6 @@ import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-me
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationObjectFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-object.factory';
import { WorkspaceObjectComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-object.comparator';
@ -36,10 +35,6 @@ export class WorkspaceSyncObjectMetadataService {
const objectMetadataRepository =
manager.getRepository(ObjectMetadataEntity);
const relationMetadataRepository = manager.getRepository(
RelationMetadataEntity,
);
// Retrieve object metadata collection from DB
const originalObjectMetadataCollection =
await objectMetadataRepository.find({
@ -50,33 +45,6 @@ export class WorkspaceSyncObjectMetadataService {
relations: ['dataSource', 'fields'],
});
// Retrieve relation metadata collection from DB
const originalRelationMetadataCollection =
await relationMetadataRepository.find({
where: {
workspaceId: context.workspaceId,
},
relations: ['toObjectMetadata', 'toFieldMetadata'],
});
const relationMetadataByFromObjectMetadataId: Record<
string,
RelationMetadataEntity[]
> = originalRelationMetadataCollection.reduce(
(acc, relationMetadata) => {
const fromObjectMetadataId = relationMetadata.fromObjectMetadataId;
if (!acc[fromObjectMetadataId]) {
acc[fromObjectMetadataId] = [];
}
acc[fromObjectMetadataId].push(relationMetadata);
return acc;
},
{} as Record<string, RelationMetadataEntity[]>,
);
// Create standard object metadata collection
const standardObjectMetadataCollection = this.standardObjectFactory.create(
standardObjectMetadataDefinitions,
@ -158,7 +126,6 @@ export class WorkspaceSyncObjectMetadataService {
await this.workspaceMigrationObjectFactory.create(
storage.objectMetadataDeleteCollection,
WorkspaceMigrationBuilderAction.DELETE,
relationMetadataByFromObjectMetadataId,
);
this.logger.log('Saving migrations');

View File

@ -1,133 +0,0 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceMigrationRelationFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-relation.factory';
import { WorkspaceRelationComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-relation.comparator';
import { StandardRelationFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-relation.factory';
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util';
@Injectable()
export class WorkspaceSyncRelationMetadataService {
constructor(
private readonly standardRelationFactory: StandardRelationFactory,
private readonly workspaceRelationComparator: WorkspaceRelationComparator,
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
private readonly workspaceMigrationRelationFactory: WorkspaceMigrationRelationFactory,
) {}
async synchronize(
context: WorkspaceSyncContext,
manager: EntityManager,
storage: WorkspaceSyncStorage,
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const objectMetadataRepository =
manager.getRepository(ObjectMetadataEntity);
// Retrieve object metadata collection from DB
const originalObjectMetadataCollection =
await objectMetadataRepository.find({
where: {
workspaceId: context.workspaceId,
},
relations: ['dataSource', 'fields'],
});
const customObjectMetadataCollection =
originalObjectMetadataCollection.filter(
(objectMetadata) => objectMetadata.isCustom,
);
// Create map of object metadata & field metadata by unique identifier
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
originalObjectMetadataCollection,
// Relation are based on the singular name
(objectMetadata) => objectMetadata.nameSingular,
);
const relationMetadataRepository = manager.getRepository(
RelationMetadataEntity,
);
// Retrieve relation metadata collection from DB
const originalRelationMetadataCollection =
await relationMetadataRepository.find({
where: {
workspaceId: context.workspaceId,
fromFieldMetadata: { isCustom: false },
},
});
// Create standard relation metadata collection
const standardRelationMetadataCollection =
this.standardRelationFactory.create(
standardObjectMetadataDefinitions,
context,
originalObjectMetadataMap,
);
const customRelationMetadataCollection =
this.standardRelationFactory.create(
customObjectMetadataCollection.map((objectMetadata) => ({
object: objectMetadata,
metadata: CustomWorkspaceEntity,
})),
context,
originalObjectMetadataMap,
);
const relationComparatorResults = this.workspaceRelationComparator.compare(
originalRelationMetadataCollection,
[
...standardRelationMetadataCollection,
...customRelationMetadataCollection,
],
);
for (const relationComparatorResult of relationComparatorResults) {
if (relationComparatorResult.action === ComparatorAction.CREATE) {
storage.addCreateRelationMetadata(relationComparatorResult.object);
} else if (relationComparatorResult.action === ComparatorAction.UPDATE) {
storage.addUpdateRelationMetadata(relationComparatorResult.object);
} else if (relationComparatorResult.action === ComparatorAction.DELETE) {
storage.addDeleteRelationMetadata(relationComparatorResult.object);
}
}
const metadataRelationUpdaterResult =
await this.workspaceMetadataUpdaterService.updateRelationMetadata(
manager,
storage,
);
// Create migrations
const createRelationWorkspaceMigrations =
await this.workspaceMigrationRelationFactory.create(
originalObjectMetadataCollection,
metadataRelationUpdaterResult.createdRelationMetadataCollection,
WorkspaceMigrationBuilderAction.CREATE,
);
const updateRelationWorkspaceMigrations =
await this.workspaceMigrationRelationFactory.create(
originalObjectMetadataCollection,
metadataRelationUpdaterResult.updatedRelationMetadataCollection,
WorkspaceMigrationBuilderAction.UPDATE,
);
return [
...createRelationWorkspaceMigrations,
...updateRelationWorkspaceMigrations,
];
}
}

View File

@ -6,7 +6,6 @@ import { ComputedPartialWorkspaceEntity } from 'src/engine/workspace-manager/wor
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
export class WorkspaceSyncStorage {
// Object metadata
@ -43,14 +42,6 @@ export class WorkspaceSyncStorage {
private readonly _fieldRelationMetadataDeleteCollection: FieldMetadataEntity<FieldMetadataType.RELATION>[] =
[];
// Relation metadata
private readonly _relationMetadataCreateCollection: Partial<RelationMetadataEntity>[] =
[];
private readonly _relationMetadataUpdateCollection: Partial<RelationMetadataEntity>[] =
[];
private readonly _relationMetadataDeleteCollection: RelationMetadataEntity[] =
[];
// Index metadata
private readonly _indexMetadataCreateCollection: Partial<IndexMetadataEntity>[] =
[];
@ -96,18 +87,6 @@ export class WorkspaceSyncStorage {
return this._fieldRelationMetadataDeleteCollection;
}
get relationMetadataCreateCollection() {
return this._relationMetadataCreateCollection;
}
get relationMetadataUpdateCollection() {
return this._relationMetadataUpdateCollection;
}
get relationMetadataDeleteCollection() {
return this._relationMetadataDeleteCollection;
}
get indexMetadataCreateCollection() {
return this._indexMetadataCreateCollection;
}
@ -168,18 +147,6 @@ export class WorkspaceSyncStorage {
this._fieldRelationMetadataDeleteCollection.push(field);
}
addCreateRelationMetadata(relation: Partial<RelationMetadataEntity>) {
this._relationMetadataCreateCollection.push(relation);
}
addUpdateRelationMetadata(relation: Partial<RelationMetadataEntity>) {
this._relationMetadataUpdateCollection.push(relation);
}
addDeleteRelationMetadata(relation: RelationMetadataEntity) {
this._relationMetadataDeleteCollection.push(relation);
}
addCreateIndexMetadata(index: Partial<IndexMetadataEntity>) {
this._indexMetadataCreateCollection.push(index);
}

View File

@ -7,7 +7,6 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
@ -22,7 +21,6 @@ import { WorkspaceSyncFieldMetadataService } from 'src/engine/workspace-manager/
import { WorkspaceSyncIndexMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-index-metadata.service';
import { WorkspaceSyncObjectMetadataIdentifiersService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service';
import { WorkspaceSyncObjectMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata.service';
import { WorkspaceSyncRelationMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-relation-metadata.service';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@Module({
@ -31,13 +29,8 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
WorkspaceMigrationBuilderModule,
WorkspaceMigrationRunnerModule,
TypeOrmModule.forFeature(
[
FieldMetadataEntity,
ObjectMetadataEntity,
RelationMetadataEntity,
WorkspaceMigrationEntity,
],
'metadata',
[FieldMetadataEntity, ObjectMetadataEntity, WorkspaceMigrationEntity],
'core',
),
DataSourceModule,
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
@ -49,7 +42,6 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
WorkspaceMetadataUpdaterService,
WorkspaceSyncObjectMetadataService,
WorkspaceSyncObjectMetadataIdentifiersService,
WorkspaceSyncRelationMetadataService,
WorkspaceSyncFieldMetadataService,
WorkspaceSyncFieldMetadataRelationService,
WorkspaceSyncMetadataService,

View File

@ -17,7 +17,6 @@ import { WorkspaceSyncFieldMetadataService } from 'src/engine/workspace-manager/
import { WorkspaceSyncIndexMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-index-metadata.service';
import { WorkspaceSyncObjectMetadataIdentifiersService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service';
import { WorkspaceSyncObjectMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata.service';
import { WorkspaceSyncRelationMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-relation-metadata.service';
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
interface SynchronizeOptions {
@ -29,11 +28,10 @@ export class WorkspaceSyncMetadataService {
private readonly logger = new Logger(WorkspaceSyncMetadataService.name);
constructor(
@InjectDataSource('metadata')
private readonly metadataDataSource: DataSource,
@InjectDataSource('core')
private readonly coreDataSource: DataSource,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceSyncObjectMetadataService: WorkspaceSyncObjectMetadataService,
private readonly workspaceSyncRelationMetadataService: WorkspaceSyncRelationMetadataService,
private readonly workspaceSyncFieldMetadataService: WorkspaceSyncFieldMetadataService,
private readonly workspaceSyncFieldMetadataRelationService: WorkspaceSyncFieldMetadataRelationService,
private readonly workspaceSyncIndexMetadataService: WorkspaceSyncIndexMetadataService,
@ -59,7 +57,7 @@ export class WorkspaceSyncMetadataService {
}> {
let workspaceMigrations: WorkspaceMigrationEntity[] = [];
const storage = new WorkspaceSyncStorage();
const queryRunner = this.metadataDataSource.createQueryRunner();
const queryRunner = this.coreDataSource.createQueryRunner();
this.logger.log('Syncing standard objects and fields metadata');