diff --git a/server/package.json b/server/package.json index bf969cee1f..4351373819 100644 --- a/server/package.json +++ b/server/package.json @@ -33,7 +33,7 @@ "database:truncate": "npx ts-node ./scripts/truncate-db.ts", "database:migrate": "yarn typeorm:migrate && yarn prisma:migrate", "database:generate": "yarn prisma:generate", - "database:seed": "yarn prisma:seed && yarn build && yarn command tenant:sync-metadata -w twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419 && yarn command tenant:migrate -w twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419 && yarn command tenant:data-seed -w twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419", + "database:seed": "yarn prisma:seed && yarn build && yarn command tenant:seed", "database:reset": "yarn database:truncate && yarn database:init", "command": "node dist/src/command" }, diff --git a/server/src/core/pipeline/seed-data/pipeline-stages.json b/server/src/core/pipeline/seed-data/pipeline-stages.json index aa50657370..aea1672925 100644 --- a/server/src/core/pipeline/seed-data/pipeline-stages.json +++ b/server/src/core/pipeline/seed-data/pipeline-stages.json @@ -2,31 +2,31 @@ { "name": "New", "color": "red", - "index": 0, + "position": 0, "type": "open" }, { "name": "Screening", "color": "purple", - "index": 1, + "position": 1, "type": "ongoing" }, { "name": "Meeting", "color": "sky", - "index": 2, + "position": 2, "type": "ongoing" }, { "name": "Proposal", "color": "turquoise", - "index": 3, + "position": 3, "type": "ongoing" }, { "name": "Customer", "color": "yellow", - "index": 4, + "position": 4, "type": "won" } ] diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata.ts b/server/src/database/typeorm-seeds/metadata/field-metadata.ts new file mode 100644 index 0000000000..19034e6107 --- /dev/null +++ b/server/src/database/typeorm-seeds/metadata/field-metadata.ts @@ -0,0 +1,334 @@ +import { DataSource } from 'typeorm'; + +const tableName = 'field_metadata'; + +export const seedFieldMetadata = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + await workspaceDataSource + .createQueryBuilder() + .insert() + .into(`${schemaName}.${tableName}`, [ + 'objectId', + 'isCustom', + 'workspaceId', + 'isActive', + 'type', + 'name', + 'label', + 'targetColumnMap', + 'description', + 'icon', + 'isNullable', + ]) + .orIgnore() + .values([ + // Companies + { + objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'name', + label: 'Name', + targetColumnMap: { + value: 'name', + }, + description: 'Name of the company', + icon: 'IconBuildingSkyscraper', + isNullable: false, + }, + { + objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'domainName', + label: 'Domain Name', + targetColumnMap: { + value: 'domainName', + }, + description: 'Domain name of the company', + icon: 'IconLink', + isNullable: true, + }, + { + objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'address', + label: 'Address', + targetColumnMap: { + value: 'address', + }, + description: 'Address of the company', + icon: 'IconMap', + isNullable: true, + }, + { + objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'employees', + label: 'Employees', + targetColumnMap: { + value: 'employees', + }, + description: 'Number of employees', + icon: 'IconUsers', + isNullable: true, + }, + // Views + { + objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'name', + label: 'Name', + targetColumnMap: { + value: 'name', + }, + description: 'View name', + icon: null, + isNullable: false, + }, + { + objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'objectId', + label: 'Object Id', + targetColumnMap: { + value: 'objectId', + }, + description: 'View target object', + icon: null, + isNullable: false, + }, + { + objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'type', + label: 'Type', + targetColumnMap: { + value: 'type', + }, + description: 'View type', + icon: null, + isNullable: false, + }, + // View Fields + { + objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Field target field', + icon: null, + isNullable: false, + }, + { + objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Field related view', + icon: null, + isNullable: false, + }, + { + objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'boolean', + name: 'isVisible', + label: 'Visible', + targetColumnMap: { + value: 'isVisible', + }, + description: 'View Field visibility', + icon: null, + isNullable: false, + }, + { + objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'number', + name: 'size', + label: 'Size', + targetColumnMap: { + value: 'size', + }, + description: 'View Field size', + icon: null, + isNullable: false, + }, + { + objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'number', + name: 'position', + label: 'Position', + targetColumnMap: { + value: 'position', + }, + description: 'View Field position', + icon: null, + isNullable: false, + }, + // View Filters + { + objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Filter target field', + icon: null, + isNullable: false, + }, + { + objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Filter related view', + icon: null, + isNullable: false, + }, + { + objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'operand', + label: 'Operand', + targetColumnMap: { + value: 'operand', + }, + description: 'View Filter operand', + icon: null, + isNullable: false, + }, + { + objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'value', + label: 'Value', + targetColumnMap: { + value: 'value', + }, + description: 'View Filter value', + icon: null, + isNullable: false, + }, + { + objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'displayValue', + label: 'Display Value', + targetColumnMap: { + value: 'displayValue', + }, + description: 'View Filter Display Value', + icon: null, + isNullable: false, + }, + // View Sorts + { + objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Sort target field', + icon: null, + isNullable: false, + }, + { + objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Sort related view', + icon: null, + isNullable: false, + }, + { + objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + isCustom: false, + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + isActive: true, + type: 'text', + name: 'direction', + label: 'Direction', + targetColumnMap: { + value: 'direction', + }, + description: 'View Sort direction', + icon: null, + isNullable: false, + }, + ]) + .execute(); +}; diff --git a/server/src/database/typeorm-seeds/metadata/object-metadata.ts b/server/src/database/typeorm-seeds/metadata/object-metadata.ts new file mode 100644 index 0000000000..25131b86c9 --- /dev/null +++ b/server/src/database/typeorm-seeds/metadata/object-metadata.ts @@ -0,0 +1,93 @@ +import { DataSource } from 'typeorm'; + +const tableName = 'object_metadata'; + +export const seedObjectMetadata = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + await workspaceDataSource + .createQueryBuilder() + .insert() + .into(`${schemaName}.${tableName}`, [ + 'id', + 'nameSingular', + 'namePlural', + 'labelSingular', + 'labelPlural', + 'targetTableName', + 'description', + 'icon', + 'dataSourceId', + 'workspaceId', + ]) + .orIgnore() + .values([ + // Companies + { + id: '1a8487a0-480c-434e-b4c7-e22408b97047', + nameSingular: 'companyV2', + namePlural: 'companiesV2', + labelSingular: 'Company', + labelPlural: 'Companies', + targetTableName: 'company', + description: 'A company', + icon: 'IconBuildingSkyscraper', + dataSourceId: 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + }, + // Views + { + id: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + nameSingular: 'viewV2', + namePlural: 'viewsV2', + labelSingular: 'View', + labelPlural: 'Views', + targetTableName: 'view', + description: '(System) Views', + icon: 'IconLayoutCollage', + dataSourceId: 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + }, + // ViewFields + { + id: '61d9000b-485c-4c48-a22e-0d9a164f9647', + nameSingular: 'viewFieldV2', + namePlural: 'viewFieldsV2', + labelSingular: 'View Field', + labelPlural: 'View Fields', + targetTableName: 'viewField', + description: '(System) View Fields', + icon: 'IconColumns3', + dataSourceId: 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + }, + // ViewFilters + { + id: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + nameSingular: 'viewFilterV2', + namePlural: 'viewFiltersV2', + labelSingular: 'View Filter', + labelPlural: 'View Filters', + targetTableName: 'viewFilter', + description: '(System) View Filters', + icon: 'IconFilterBolt', + dataSourceId: 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + }, + // ViewSorts + { + id: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + nameSingular: 'viewSortV2', + namePlural: 'viewSortsV2', + labelSingular: 'View Sort', + labelPlural: 'View Sorts', + targetTableName: 'viewSort', + description: '(System) View Sorts', + icon: 'IconArrowsSort', + dataSourceId: 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', + workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + }, + ]) + .execute(); +}; diff --git a/server/src/database/typeorm-seeds/tenant/companies.ts b/server/src/database/typeorm-seeds/tenant/companies.ts new file mode 100644 index 0000000000..f907a04cef --- /dev/null +++ b/server/src/database/typeorm-seeds/tenant/companies.ts @@ -0,0 +1,95 @@ +import { DataSource } from 'typeorm'; + +const tableName = 'company'; + +export const seedCompanies = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + await workspaceDataSource + .createQueryBuilder() + .insert() + .into(`${schemaName}.${tableName}`, ['id', 'name', 'domainName', 'address']) + .orIgnore() + .values([ + { + id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408', + name: 'Linkedin', + domainName: 'linkedin.com', + address: '', + }, + { + id: '118995f3-5d81-46d6-bf83-f7fd33ea6102', + name: 'Facebook', + domainName: 'facebook.com', + address: '', + }, + { + id: '04b2e9f5-0713-40a5-8216-82802401d33e', + name: 'Qonto', + domainName: 'qonto.com', + address: '', + }, + { + id: '460b6fb1-ed89-413a-b31a-962986e67bb4', + name: 'Microsoft', + domainName: 'microsoft.com', + address: '', + }, + { + id: '89bb825c-171e-4bcc-9cf7-43448d6fb278', + name: 'Airbnb', + domainName: 'airbnb.com', + address: '', + }, + { + id: '0d940997-c21e-4ec2-873b-de4264d89025', + name: 'Google', + domainName: 'google.com', + address: '', + }, + { + id: '1d3a1c6e-707e-44dc-a1d2-30030bf1a944', + name: 'Netflix', + domainName: 'netflix.com', + address: '', + }, + { + id: '7a93d1e5-3f74-492d-a101-2a70f50a1645', + name: 'Libeo', + domainName: 'libeo.io', + address: '', + }, + { + id: '9d162de6-cfbf-4156-a790-e39854dcd4eb', + name: 'Claap', + domainName: 'claap.io', + address: '', + }, + { + id: 'aaffcfbd-f86b-419f-b794-02319abe8637', + name: 'Hasura', + domainName: 'hasura.io', + address: '', + }, + { + id: 'f33dc242-5518-4553-9433-42d8eb82834b', + name: 'Wework', + domainName: 'wework.com', + address: '', + }, + { + id: 'a7bc68d5-f79e-40dd-bd06-c36e6abb4678', + name: 'Samsung', + domainName: 'samsung.com', + address: '', + }, + { + id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d', + name: 'Algolia', + domainName: 'algolia.com', + address: '', + }, + ]) + .execute(); +}; diff --git a/server/src/database/typeorm-seeds/tenant/view-fields.ts b/server/src/database/typeorm-seeds/tenant/view-fields.ts new file mode 100644 index 0000000000..987a5ce8a1 --- /dev/null +++ b/server/src/database/typeorm-seeds/tenant/view-fields.ts @@ -0,0 +1,160 @@ +import { DataSource } from 'typeorm'; + +const tableName = 'viewField'; + +export const seedViewFields = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + await workspaceDataSource + .createQueryBuilder() + .insert() + .into(`${schemaName}.${tableName}`, [ + 'id', + 'fieldId', + 'viewId', + 'position', + 'isVisible', + 'size', + ]) + .orIgnore() + .values([ + { + id: '46a72a5b-276e-4241-a05f-c054410aebcb', + fieldId: 'name', + viewId: '10bec73c-0aea-4cc4-a3b2-8c2186f29b43', + position: 0, + isVisible: true, + size: 180, + }, + { + id: 'f15b26ff-8f79-49dd-8f53-4286dd1af846', + fieldId: 'name', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 0, + isVisible: true, + size: 180, + }, + { + id: '8d1dbb50-c97f-42c4-8575-3d2c9bdeb6e5', + fieldId: 'domainName', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 1, + isVisible: true, + size: 100, + }, + { + id: '33833b3b-4e02-4f10-91fc-c594422952af', + fieldId: 'accountOwner', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 2, + isVisible: true, + size: 150, + }, + { + id: 'c750a968-832e-4812-a1a2-74f515af55c1', + fieldId: 'createdAt', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 3, + isVisible: true, + size: 150, + }, + { + id: '2fde3187-a0bc-47ca-80bd-457bd826fb4a', + fieldId: 'employees', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 4, + isVisible: true, + size: 150, + }, + { + id: '2fead26f-3f4f-4a4d-a4c6-3abe7b2f74c9', + fieldId: 'linkedin', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 5, + isVisible: true, + size: 170, + }, + { + id: '0cffa82a-c851-4e17-b46c-2c4642d78329', + fieldId: 'address', + viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80', + position: 6, + isVisible: true, + size: 170, + }, + { + id: '93a68c4a-8107-409a-9adb-06305ffbd692', + fieldId: 'displayName', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 0, + isVisible: true, + size: 210, + }, + { + id: 'd955ee31-6316-4cb2-af71-9609dede4d7e', + fieldId: 'email', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 1, + isVisible: true, + size: 150, + }, + { + id: 'bceb4d84-8ad1-4a0e-9333-efb870b42eb8', + fieldId: 'company', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 2, + isVisible: true, + size: 150, + }, + { + id: 'bef874d4-f349-4cdb-ae28-6e9fc497449b', + fieldId: 'phone', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 3, + isVisible: true, + size: 150, + }, + { + id: 'e06f920d-1af9-404d-8b9a-4f97c4009a4a', + fieldId: 'createdAt', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 4, + isVisible: true, + size: 150, + }, + { + id: '92d94ee8-31fc-4025-a427-29291abb2b19', + fieldId: 'city', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 5, + isVisible: true, + size: 150, + }, + { + id: 'b38e4022-1559-40da-bd5e-29d89b6c8330', + fieldId: 'jobTitle', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 6, + isVisible: true, + size: 150, + }, + { + id: '30147fab-9666-4db5-a11b-20af4544c712', + fieldId: 'linkedin', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 7, + isVisible: true, + size: 150, + }, + { + id: 'f0870949-21ac-46a2-b3ec-d1b0107c434c', + fieldId: 'x', + viewId: '6095799e-b48f-4e00-b071-10818083593a', + position: 8, + isVisible: true, + size: 150, + }, + ]) + .execute(); +}; diff --git a/server/src/database/typeorm-seeds/tenant/views.ts b/server/src/database/typeorm-seeds/tenant/views.ts new file mode 100644 index 0000000000..6f253d3418 --- /dev/null +++ b/server/src/database/typeorm-seeds/tenant/views.ts @@ -0,0 +1,41 @@ +import { DataSource } from 'typeorm'; + +const tableName = 'view'; + +export const seedViews = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + await workspaceDataSource + .createQueryBuilder() + .insert() + .into(`${schemaName}.${tableName}`, ['id', 'name', 'objectId', 'type']) + .orIgnore() + .values([ + { + id: '37a8a866-eb17-4e76-9382-03143a2f6a80', + name: 'All companies', + objectId: 'company', + type: 'table', + }, + { + id: '6095799e-b48f-4e00-b071-10818083593a', + name: 'All people', + objectId: 'person', + type: 'table', + }, + { + id: 'e26f66b7-f890-4a5c-b4d2-ec09987b5308', + name: 'All opportunities', + objectId: 'company', + type: 'kanban', + }, + { + id: '10bec73c-0aea-4cc4-a3b2-8c2186f29b43', + name: 'All Companies (V2)', + objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + type: 'table', + }, + ]) + .execute(); +}; diff --git a/server/src/metadata/commands/data-seed-tenant.command.ts b/server/src/metadata/commands/data-seed-tenant.command.ts index aa14737c89..107aa917dc 100644 --- a/server/src/metadata/commands/data-seed-tenant.command.ts +++ b/server/src/metadata/commands/data-seed-tenant.command.ts @@ -1,47 +1,69 @@ -import { Command, CommandRunner, Option } from 'nest-commander'; +import { InjectDataSource } from '@nestjs/typeorm'; + +import { Command, CommandRunner } from 'nest-commander'; +import { DataSource } from 'typeorm'; import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { TenantInitialisationService } from 'src/metadata/tenant-initialisation/tenant-initialisation.service'; +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; +import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; +import { seedCompanies } from 'src/database/typeorm-seeds/tenant/companies'; +import { seedViewFields } from 'src/database/typeorm-seeds/tenant/view-fields'; +import { seedViews } from 'src/database/typeorm-seeds/tenant/views'; +import { seedFieldMetadata } from 'src/database/typeorm-seeds/metadata/field-metadata'; +import { seedObjectMetadata } from 'src/database/typeorm-seeds/metadata/object-metadata'; // TODO: implement dry-run -interface DataSeedTenantOptions { - workspaceId: string; -} - @Command({ - name: 'tenant:data-seed', - description: 'Seed tenant with initial data', + name: 'tenant:seed', + description: + 'Seed tenant with initial data. This command is intended for development only.', }) export class DataSeedTenantCommand extends CommandRunner { + workspaceId = 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'; + constructor( + @InjectDataSource('metadata') + private readonly metadataDataSource: DataSource, private readonly dataSourceMetadataService: DataSourceMetadataService, - private readonly tenantInitialisationService: TenantInitialisationService, + private readonly dataSourceService: DataSourceService, + private readonly tenantMigrationService: TenantMigrationService, + private readonly migrationRunnerService: MigrationRunnerService, ) { super(); } - async run( - _passedParam: string[], - options: DataSeedTenantOptions, - ): Promise { + async run(): Promise { const dataSourceMetadata = await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail( - options.workspaceId, + this.workspaceId, ); - // TODO: run in a dedicated job + run queries in a transaction. - await this.tenantInitialisationService.prefillWorkspaceWithStandardObjects( - dataSourceMetadata, - options.workspaceId, + + const workspaceDataSource = + await this.dataSourceService.connectToWorkspaceDataSource( + this.workspaceId, + ); + + if (!workspaceDataSource) { + throw new Error('Could not connect to workspace data source'); + } + + await seedObjectMetadata(this.metadataDataSource, 'metadata'); + await seedFieldMetadata(this.metadataDataSource, 'metadata'); + + await this.tenantMigrationService.insertStandardMigrations( + this.workspaceId, + ); + await this.migrationRunnerService.executeMigrationFromPendingMigrations( + this.workspaceId, + ); + + await seedCompanies(workspaceDataSource, dataSourceMetadata.schema); + await seedViewFields(workspaceDataSource, dataSourceMetadata.schema); + await seedViews(workspaceDataSource, dataSourceMetadata.schema); + + await this.dataSourceService.disconnectFromWorkspaceDataSource( + this.workspaceId, ); } - - // TODO: workspaceId should be optional and we should run migrations for all workspaces - @Option({ - flags: '-w, --workspace-id [workspace_id]', - description: 'workspace id', - required: true, - }) - parseWorkspaceId(value: string): string { - return value; - } } diff --git a/server/src/metadata/commands/metadata-command.module.ts b/server/src/metadata/commands/metadata-command.module.ts index 0ae736111b..14f0919c22 100644 --- a/server/src/metadata/commands/metadata-command.module.ts +++ b/server/src/metadata/commands/metadata-command.module.ts @@ -6,6 +6,7 @@ import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metada import { FieldMetadataModule } from 'src/metadata/field-metadata/field-metadata.module'; import { TenantInitialisationModule } from 'src/metadata/tenant-initialisation/tenant-initialisation.module'; import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; import { SyncTenantMetadataCommand } from './sync-tenant-metadata.command'; import { RunTenantMigrationsCommand } from './run-tenant-migrations.command'; @@ -19,6 +20,7 @@ import { DataSeedTenantCommand } from './data-seed-tenant.command'; FieldMetadataModule, DataSourceMetadataModule, TenantInitialisationModule, + DataSourceModule, ], providers: [ RunTenantMigrationsCommand, diff --git a/server/src/metadata/commands/sync-tenant-metadata.command.ts b/server/src/metadata/commands/sync-tenant-metadata.command.ts index 28b6a86c45..ffa299d275 100644 --- a/server/src/metadata/commands/sync-tenant-metadata.command.ts +++ b/server/src/metadata/commands/sync-tenant-metadata.command.ts @@ -1,7 +1,6 @@ import { Command, CommandRunner, Option } from 'nest-commander'; import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; -import { TenantInitialisationService } from 'src/metadata/tenant-initialisation/tenant-initialisation.service'; import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; // TODO: implement dry-run @@ -17,7 +16,6 @@ export class SyncTenantMetadataCommand extends CommandRunner { constructor( private readonly objectMetadataService: ObjectMetadataService, private readonly dataSourceMetadataService: DataSourceMetadataService, - private readonly tenantInitialisationService: TenantInitialisationService, ) { super(); } @@ -37,8 +35,7 @@ export class SyncTenantMetadataCommand extends CommandRunner { workspaceId: { eq: options.workspaceId }, }); - // TODO: this should not be the responsibility of tenantInitialisationService. - await this.tenantInitialisationService.createObjectsAndFieldsMetadata( + await this.objectMetadataService.createStandardObjectsAndFieldsMetadata( dataSourceMetadata.id, options.workspaceId, ); diff --git a/server/src/metadata/data-source/data-source.service.ts b/server/src/metadata/data-source/data-source.service.ts index deb7ee8b66..f53e3703d5 100644 --- a/server/src/metadata/data-source/data-source.service.ts +++ b/server/src/metadata/data-source/data-source.service.ts @@ -88,9 +88,6 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy { await workspaceDataSource.initialize(); - // Set search path to workspace schema for raw queries - await workspaceDataSource?.query(`SET search_path TO ${schema};`); - this.dataSources.set(workspaceId, workspaceDataSource); return workspaceDataSource; diff --git a/server/src/metadata/metadata.datasource.ts b/server/src/metadata/metadata.datasource.ts index 1bbd11ca0d..08d5b3c9cc 100644 --- a/server/src/metadata/metadata.datasource.ts +++ b/server/src/metadata/metadata.datasource.ts @@ -22,7 +22,7 @@ const configService = new ConfigService(); export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = { url: configService.get('PG_DATABASE_URL'), type: 'postgres', - logging: false, + logging: ['query', 'error'], schema: 'metadata', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: false, diff --git a/server/src/metadata/object-metadata/services/object-metadata.service.ts b/server/src/metadata/object-metadata/services/object-metadata.service.ts index 6200534b32..cc113c6560 100644 --- a/server/src/metadata/object-metadata/services/object-metadata.service.ts +++ b/server/src/metadata/object-metadata/services/object-metadata.service.ts @@ -13,6 +13,7 @@ import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-mig import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; +import { standardObjectsMetadata } from 'src/metadata/standard-objects/standard-object-metadata'; @Injectable() export class ObjectMetadataService extends TypeOrmQueryService { @@ -84,4 +85,32 @@ export class ObjectMetadataService extends TypeOrmQueryService { where: { id: objectMetadataId, workspaceId }, }); } + + /** + * + * Create all standard objects and fields metadata for a given workspace + * + * @param dataSourceMetadataId + * @param workspaceId + */ + public async createStandardObjectsAndFieldsMetadata( + dataSourceMetadataId: string, + workspaceId: string, + ) { + await this.objectMetadataRepository.save( + Object.values(standardObjectsMetadata).map((objectMetadata) => ({ + ...objectMetadata, + dataSourceId: dataSourceMetadataId, + workspaceId, + isCustom: false, + isActive: true, + fields: objectMetadata.fields.map((field) => ({ + ...field, + workspaceId, + isCustom: false, + isActive: true, + })), + })), + ); + } } diff --git a/server/src/metadata/standard-objects/companies/companies.metadata.ts b/server/src/metadata/standard-objects/companies/companies.metadata.ts new file mode 100644 index 0000000000..8ef69cc0f2 --- /dev/null +++ b/server/src/metadata/standard-objects/companies/companies.metadata.ts @@ -0,0 +1,57 @@ +const companiesMetadata = { + nameSingular: 'companyV2', + namePlural: 'companiesV2', + labelSingular: 'Company', + labelPlural: 'Companies', + targetTableName: 'company', + description: 'A company', + icon: 'IconBuildingSkyscraper', + fields: [ + { + type: 'text', + name: 'name', + label: 'Name', + targetColumnMap: { + value: 'name', + }, + description: 'Name of the company', + icon: 'IconBuildingSkyscraper', + isNullable: false, + }, + { + type: 'text', + name: 'domainName', + label: 'Domain Name', + targetColumnMap: { + value: 'domainName', + }, + description: 'Domain name of the company', + icon: 'IconLink', + isNullable: true, + }, + { + type: 'text', + name: 'address', + label: 'Address', + targetColumnMap: { + value: 'address', + }, + description: 'Address of the company', + icon: 'IconMap', + isNullable: true, + }, + { + type: 'number', + name: 'employees', + label: 'Employees', + targetColumnMap: { + value: 'employees', + }, + description: 'Number of employees', + icon: 'IconUsers', + isNullable: true, + }, + ], +}; + +export default companiesMetadata; diff --git a/server/src/metadata/standard-objects/standard-object-metadata.ts b/server/src/metadata/standard-objects/standard-object-metadata.ts new file mode 100644 index 0000000000..6cc8925e02 --- /dev/null +++ b/server/src/metadata/standard-objects/standard-object-metadata.ts @@ -0,0 +1,13 @@ +import companiesMetadata from './companies/companies.metadata'; +import viewFieldsMetadata from './view-fields/view-fields.metadata'; +import viewFiltersMetadata from './view-filters/view-filters.metadata'; +import viewSortsMetadata from './view-sorts/view-sorts.metadata'; +import viewsMetadata from './views/views.metadata'; + +export const standardObjectsMetadata = { + companyV2: companiesMetadata, + viewV2: viewsMetadata, + viewFieldV2: viewFieldsMetadata, + viewFilterV2: viewFiltersMetadata, + viewSortV2: viewSortsMetadata, +}; diff --git a/server/src/metadata/standard-objects/view-fields/view-fields.metadata.ts b/server/src/metadata/standard-objects/view-fields/view-fields.metadata.ts new file mode 100644 index 0000000000..b39a281f48 --- /dev/null +++ b/server/src/metadata/standard-objects/view-fields/view-fields.metadata.ts @@ -0,0 +1,68 @@ +const viewFieldsMetadata = { + nameSingular: 'viewFieldV2', + namePlural: 'viewFieldsV2', + labelSingular: 'View Field', + labelPlural: 'View Fields', + targetTableName: 'viewField', + description: '(System) View Fields', + icon: 'IconColumns3', + fields: [ + { + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Field target field', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Field related view', + icon: null, + isNullable: false, + }, + { + type: 'boolean', + name: 'isVisible', + label: 'Visible', + targetColumnMap: { + value: 'isVisible', + }, + description: 'View Field visibility', + icon: null, + isNullable: false, + }, + { + type: 'number', + name: 'size', + label: 'Size', + targetColumnMap: { + value: 'size', + }, + description: 'View Field size', + icon: null, + isNullable: false, + }, + { + type: 'number', + name: 'position', + label: 'Position', + targetColumnMap: { + value: 'position', + }, + description: 'View Field position', + icon: null, + isNullable: false, + }, + ], +}; + +export default viewFieldsMetadata; diff --git a/server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts b/server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts new file mode 100644 index 0000000000..d6188c583a --- /dev/null +++ b/server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts @@ -0,0 +1,68 @@ +const viewFiltersMetadata = { + nameSingular: 'viewFilterV2', + namePlural: 'viewFiltersV2', + labelSingular: 'View Filter', + labelPlural: 'View Filters', + targetTableName: 'viewFilter', + description: '(System) View Filters', + icon: 'IconFilterBolt', + fields: [ + { + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Filter target field', + icon: null, + isNullable: true, + }, + { + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Filter related view', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'operand', + label: 'Operand', + targetColumnMap: { + value: 'operand', + }, + description: 'View Filter operand', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'value', + label: 'Value', + targetColumnMap: { + value: 'value', + }, + description: 'View Filter value', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'displayValue', + label: 'Display Value', + targetColumnMap: { + value: 'displayValue', + }, + description: 'View Filter Display Value', + icon: null, + isNullable: false, + }, + ], +}; + +export default viewFiltersMetadata; diff --git a/server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts b/server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts new file mode 100644 index 0000000000..a326f314c6 --- /dev/null +++ b/server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts @@ -0,0 +1,46 @@ +const viewSortsMetadata = { + nameSingular: 'viewSortV2', + namePlural: 'viewSortsV2', + labelSingular: 'View Sort', + labelPlural: 'View Sorts', + targetTableName: 'viewSort', + description: '(System) View Sorts', + icon: 'IconArrowsSort', + fields: [ + { + type: 'text', + name: 'fieldId', + label: 'Field Id', + targetColumnMap: { + value: 'fieldId', + }, + description: 'View Sort target field', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'viewId', + label: 'View Id', + targetColumnMap: { + value: 'viewId', + }, + description: 'View Sort related view', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'direction', + label: 'Direction', + targetColumnMap: { + value: 'direction', + }, + description: 'View Sort direction', + icon: null, + isNullable: false, + }, + ], +}; + +export default viewSortsMetadata; diff --git a/server/src/metadata/standard-objects/views/views.metadata.ts b/server/src/metadata/standard-objects/views/views.metadata.ts new file mode 100644 index 0000000000..130a7f810a --- /dev/null +++ b/server/src/metadata/standard-objects/views/views.metadata.ts @@ -0,0 +1,46 @@ +const viewsMetadata = { + nameSingular: 'viewV2', + namePlural: 'viewsV2', + labelSingular: 'View', + labelPlural: 'Views', + targetTableName: 'view', + description: '(System) Views', + icon: 'IconLayoutCollage', + fields: [ + { + type: 'text', + name: 'name', + label: 'Name', + targetColumnMap: { + value: 'name', + }, + description: 'View name', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'objectId', + label: 'Object Id', + targetColumnMap: { + value: 'objectId', + }, + description: 'View target object', + icon: null, + isNullable: false, + }, + { + type: 'text', + name: 'type', + label: 'Type', + targetColumnMap: { + value: 'type', + }, + description: 'View type', + icon: null, + isNullable: false, + }, + ], +}; + +export default viewsMetadata; diff --git a/server/src/metadata/tenant-initialisation/standard-objects-prefill-data/standard-objects-prefill-data.ts b/server/src/metadata/tenant-initialisation/standard-objects-prefill-data/standard-objects-prefill-data.ts new file mode 100644 index 0000000000..10e2929853 --- /dev/null +++ b/server/src/metadata/tenant-initialisation/standard-objects-prefill-data/standard-objects-prefill-data.ts @@ -0,0 +1,269 @@ +import { DataSource, EntityManager } from 'typeorm'; + +export const standardObjectsPrefillData = async ( + workspaceDataSource: DataSource, + schemaName: string, +) => { + workspaceDataSource.transaction(async (entityManager: EntityManager) => { + const createdCompanies = await entityManager + .createQueryBuilder() + .insert() + .into(`${schemaName}.company`, [ + 'name', + 'domainName', + 'address', + 'employees', + ]) + .orIgnore() + .values([ + { + name: 'Airbnb', + domainName: 'airbnb.com', + address: 'San Francisco', + employees: 5000, + }, + { + name: 'Qonto', + domainName: 'qonto.com', + address: 'San Francisco', + employees: 800, + }, + { + name: 'Stripe', + domainName: 'stripe.com', + address: 'San Francisco', + employees: 8000, + }, + { + name: 'Figma', + domainName: 'figma.com', + address: 'San Francisco', + employees: 800, + }, + { + name: 'Notion', + domainName: 'notion.com', + address: 'San Francisco', + employees: 400, + }, + ]) + .returning('*') + .execute(); + + const companyIdMap = createdCompanies.raw.reduce((acc, view) => { + acc[view.name] = view.id; + return acc; + }, {}); + + const createdViews = await entityManager + .createQueryBuilder() + .insert() + .into(`${schemaName}.view`, ['name', 'objectId', 'type']) + .orIgnore() + .values([ + { + name: 'All companies', + objectId: 'company', + type: 'table', + }, + { + name: 'All people', + objectId: 'person', + type: 'table', + }, + { + name: 'All opportunities', + objectId: 'company', + type: 'kanban', + }, + { + name: 'All Companies (V2)', + objectId: companyIdMap['Airbnb'], + type: 'table', + }, + ]) + .returning('*') + .execute(); + + const viewIdMap = createdViews.raw.reduce((acc, view) => { + acc[view.name] = view.id; + return acc; + }, {}); + + await entityManager + .createQueryBuilder() + .insert() + .into(`${schemaName}.viewField`, [ + 'fieldId', + 'viewId', + 'position', + 'isVisible', + 'size', + ]) + .orIgnore() + .values([ + { + fieldId: 'name', + viewId: viewIdMap['All Companies (V2)'], + position: 0, + isVisible: true, + size: 180, + }, + { + fieldId: 'name', + viewId: viewIdMap['All companies'], + position: 0, + isVisible: true, + size: 180, + }, + { + fieldId: 'domainName', + viewId: viewIdMap['All companies'], + position: 1, + isVisible: true, + size: 100, + }, + { + fieldId: 'accountOwner', + viewId: viewIdMap['All companies'], + position: 2, + isVisible: true, + size: 150, + }, + { + fieldId: 'createdAt', + viewId: viewIdMap['All companies'], + position: 3, + isVisible: true, + size: 150, + }, + { + fieldId: 'employees', + viewId: viewIdMap['All companies'], + position: 4, + isVisible: true, + size: 150, + }, + { + fieldId: 'linkedin', + viewId: viewIdMap['All companies'], + position: 5, + isVisible: true, + size: 170, + }, + { + fieldId: 'address', + viewId: viewIdMap['All companies'], + position: 6, + isVisible: true, + size: 170, + }, + { + fieldId: 'displayName', + viewId: viewIdMap['All people'], + position: 0, + isVisible: true, + size: 210, + }, + { + fieldId: 'email', + viewId: viewIdMap['All people'], + position: 1, + isVisible: true, + size: 150, + }, + { + fieldId: 'company', + viewId: viewIdMap['All people'], + position: 2, + isVisible: true, + size: 150, + }, + { + fieldId: 'phone', + viewId: viewIdMap['All people'], + position: 3, + isVisible: true, + size: 150, + }, + { + fieldId: 'createdAt', + viewId: viewIdMap['All people'], + position: 4, + isVisible: true, + size: 150, + }, + { + fieldId: 'city', + viewId: viewIdMap['All people'], + position: 5, + isVisible: true, + size: 150, + }, + { + fieldId: 'jobTitle', + viewId: viewIdMap['All people'], + position: 6, + isVisible: true, + size: 150, + }, + { + fieldId: 'linkedin', + viewId: viewIdMap['All people'], + position: 7, + isVisible: true, + size: 150, + }, + { + fieldId: 'x', + viewId: viewIdMap['All people'], + position: 8, + isVisible: true, + size: 150, + }, + { + fieldId: 'amount', + viewId: viewIdMap['All opportunities'], + position: 0, + isVisible: true, + size: 180, + }, + { + fieldId: 'probability', + viewId: viewIdMap['All opportunities'], + position: 1, + isVisible: true, + size: 150, + }, + { + fieldId: 'closeDate', + viewId: viewIdMap['All opportunities'], + position: 2, + isVisible: true, + size: 100, + }, + { + fieldId: 'company', + viewId: viewIdMap['All opportunities'], + position: 3, + isVisible: true, + size: 150, + }, + { + fieldId: 'createdAt', + viewId: viewIdMap['All opportunities'], + position: 4, + isVisible: true, + size: 150, + }, + { + fieldId: 'pointOfContact', + viewId: viewIdMap['All opportunities'], + position: 5, + isVisible: true, + size: 150, + }, + ]) + .execute(); + }); +}; diff --git a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json deleted file mode 100644 index 7fe4923e4d..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "id": "1a8487a0-480c-434e-b4c7-e22408b97047", - "nameSingular": "companyV2", - "namePlural": "companiesV2", - "labelSingular": "Company", - "labelPlural": "Companies", - "targetTableName": "company", - "description": "A company", - "icon": "IconBuildingSkyscraper", - "fields": [ - { - "type": "text", - "name": "name", - "label": "Name", - "targetColumnMap": { - "value": "name" - }, - "description": "Name of the company", - "icon": "IconBuildingSkyscraper", - "isNullable": false - }, - { - "type": "text", - "name": "domainName", - "label": "Domain Name", - "targetColumnMap": { - "value": "domainName" - }, - "description": "Domain name of the company", - "icon": "IconLink", - "isNullable": true - }, - { - "type": "text", - "name": "address", - "label": "Address", - "targetColumnMap": { - "value": "address" - }, - "description": "Address of the company", - "icon": "IconMap", - "isNullable": true - }, - { - "type": "number", - "name": "employees", - "label": "Employees", - "targetColumnMap": { - "value": "employees" - }, - "description": "Number of employees", - "icon": "IconUsers", - "isNullable": true - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.seeds.json b/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.seeds.json deleted file mode 100644 index 883253bab3..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.seeds.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "name": "Airbnb", - "domainName": "airbnb.com", - "address": "San Francisco", - "employees": 5000 - }, - { - "name": "Qonto", - "domainName": "qonto.com", - "address": "San Francisco", - "employees": 800 - }, - { - "name": "Stripe", - "domainName": "stripe.com", - "address": "San Francisco", - "employees": 8000 - }, - { - "name": "Figma", - "domainName": "figma.com", - "address": "San Francisco", - "employees": 800 - }, - { - "name": "Notion", - "domainName": "notion.com", - "address": "San Francisco", - "employees": 400 - } -] diff --git a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts b/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts deleted file mode 100644 index 0ec54d2ba0..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts +++ /dev/null @@ -1,13 +0,0 @@ -import companyObject from './companies/companies.metadata.json'; -import viewObject from './views/views.metadata.json'; -import viewFieldObject from './view-fields/view-fields.metadata.json'; -import viewFilterObject from './view-filters/view-filters.metadata.json'; -import viewSortObject from './view-sorts/view-sorts.metadata.json'; - -export const standardObjectsMetadata = { - companyV2: companyObject, - viewV2: viewObject, - viewFieldV2: viewFieldObject, - viewFilterV2: viewFilterObject, - viewSortV2: viewSortObject, -}; diff --git a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-seeds.ts b/server/src/metadata/tenant-initialisation/standard-objects/standard-object-seeds.ts deleted file mode 100644 index 92db850d3c..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-seeds.ts +++ /dev/null @@ -1,9 +0,0 @@ -import companySeeds from './companies/companies.seeds.json'; -import viewSeeds from './views/views.seeds.json'; -import viewFieldSeeds from './view-fields/view-fields.seeds.json'; - -export const standardObjectsSeeds = { - companyV2: companySeeds, - viewV2: viewSeeds, - viewFieldV2: viewFieldSeeds, -}; diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json deleted file mode 100644 index ddbe9cb833..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "nameSingular": "viewFieldV2", - "namePlural": "viewFieldsV2", - "labelSingular": "View Field", - "labelPlural": "View Fields", - "targetTableName": "viewField", - "description": "(System) View Fields", - "icon": "IconColumns3", - "fields": [ - { - "type": "text", - "name": "fieldId", - "label": "Field Id", - "targetColumnMap": { - "value": "fieldId" - }, - "description": "View Field target field", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "viewId", - "label": "View Id", - "targetColumnMap": { - "value": "viewId" - }, - "description": "View Field related view", - "icon": null, - "isNullable": false - }, - { - "type": "boolean", - "name": "isVisible", - "label": "Visible", - "targetColumnMap": { - "value": "isVisible" - }, - "description": "View Field visibility", - "icon": null, - "isNullable": false - }, - { - "type": "number", - "name": "size", - "label": "Size", - "targetColumnMap": { - "value": "size" - }, - "description": "View Field size", - "icon": null, - "isNullable": false - }, - { - "type": "number", - "name": "position", - "label": "Position", - "targetColumnMap": { - "value": "position" - }, - "description": "View Field position", - "icon": null, - "isNullable": false - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.seeds.json b/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.seeds.json deleted file mode 100644 index 47059d39f5..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.seeds.json +++ /dev/null @@ -1,166 +0,0 @@ -[ - { - "fieldId": "name", - "viewId": "10bec73c-0aea-4cc4-a3b2-8c2186f29b43", - "position": 0, - "isVisible": true, - "size": 180 - }, - - { - "fieldId": "name", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 0, - "isVisible": true, - "size": 180 - }, - { - "fieldId": "domainName", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 1, - "isVisible": true, - "size": 100 - }, - { - "fieldId": "accountOwner", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 2, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "createdAt", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 3, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "employees", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 4, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "linkedin", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 5, - "isVisible": true, - "size": 170 - }, - { - "fieldId": "address", - "viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "position": 6, - "isVisible": true, - "size": 170 - }, - - { - "fieldId": "displayName", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 0, - "isVisible": true, - "size": 210 - }, - { - "fieldId": "email", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 1, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "company", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 2, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "phone", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 3, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "createdAt", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 4, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "city", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 5, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "jobTitle", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 6, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "linkedin", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 7, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "x", - "viewId": "6095799e-b48f-4e00-b071-10818083593a", - "position": 8, - "isVisible": true, - "size": 150 - }, - - { - "fieldId": "amount", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 0, - "isVisible": true, - "size": 180 - }, - { - "fieldId": "probability", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 1, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "closeDate", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 2, - "isVisible": true, - "size": 100 - }, - { - "fieldId": "company", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 3, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "createdAt", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 4, - "isVisible": true, - "size": 150 - }, - { - "fieldId": "pointOfContact", - "viewId": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "position": 5, - "isVisible": true, - "size": 150 - } -] diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json deleted file mode 100644 index 6bbd67becd..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "nameSingular": "viewFilterV2", - "namePlural": "viewFiltersV2", - "labelSingular": "View Filter", - "labelPlural": "View Filters", - "targetTableName": "viewFilter", - "description": "(System) View Filters", - "icon": "IconFilterBolt", - "fields": [ - { - "type": "text", - "name": "fieldId", - "label": "Field Id", - "targetColumnMap": { - "value": "fieldId" - }, - "description": "View Filter target field", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "viewId", - "label": "View Id", - "targetColumnMap": { - "value": "viewId" - }, - "description": "View Filter related view", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "operand", - "label": "Operand", - "targetColumnMap": { - "value": "operand" - }, - "description": "View Filter operand", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "value", - "label": "Value", - "targetColumnMap": { - "value": "value" - }, - "description": "View Filter value", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "displayValue", - "label": "Display Value", - "targetColumnMap": { - "value": "displayValue" - }, - "description": "View Filter Display Value", - "icon": null, - "isNullable": false - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json deleted file mode 100644 index f1bf05a6a3..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "nameSingular": "viewSortV2", - "namePlural": "viewSortsV2", - "labelSingular": "View Sort", - "labelPlural": "View Sorts", - "targetTableName": "viewSort", - "description": "(System) View Sorts", - "icon": "IconArrowsSort", - "fields": [ - { - "type": "text", - "name": "fieldId", - "label": "Field Id", - "targetColumnMap": { - "value": "fieldId" - }, - "description": "View Sort target field", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "viewId", - "label": "View Id", - "targetColumnMap": { - "value": "viewId" - }, - "description": "View Sort related view", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "direction", - "label": "Direction", - "targetColumnMap": { - "value": "direction" - }, - "description": "View Sort direction", - "icon": null, - "isNullable": false - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json deleted file mode 100644 index 4f8c059a28..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "nameSingular": "viewV2", - "namePlural": "viewsV2", - "labelSingular": "View", - "labelPlural": "Views", - "targetTableName": "view", - "description": "(System) Views", - "icon": "IconLayoutCollage", - "fields": [ - { - "type": "text", - "name": "name", - "label": "Name", - "targetColumnMap": { - "value": "name" - }, - "description": "View name", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "objectId", - "label": "Object Id", - "targetColumnMap": { - "value": "objectId" - }, - "description": "View target object", - "icon": null, - "isNullable": false - }, - { - "type": "text", - "name": "type", - "label": "Type", - "targetColumnMap": { - "value": "type" - }, - "description": "View type", - "icon": null, - "isNullable": false - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/views/views.seeds.json b/server/src/metadata/tenant-initialisation/standard-objects/views/views.seeds.json deleted file mode 100644 index b9860db354..0000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/views/views.seeds.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "id": "37a8a866-eb17-4e76-9382-03143a2f6a80", - "name": "All companies", - "objectId": "company", - "type": "table" - }, - { - "id": "6095799e-b48f-4e00-b071-10818083593a", - "name": "All people", - "objectId": "person", - "type": "table" - }, - { - "id": "e26f66b7-f890-4a5c-b4d2-ec09987b5308", - "name": "All opportunities", - "objectId": "company", - "type": "kanban" - }, - { - "id": "10bec73c-0aea-4cc4-a3b2-8c2186f29b43", - "name": "All Companies (V2)", - "objectId": "1a8487a0-480c-434e-b4c7-e22408b97047", - "type": "table" - } -] diff --git a/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts b/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts index 61b976ebb7..af4e57ced9 100644 --- a/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts +++ b/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts @@ -3,9 +3,8 @@ import { Module } from '@nestjs/common'; import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; -import { FieldMetadataModule } from 'src/metadata/field-metadata/field-metadata.module'; -import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; +import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; import { TenantInitialisationService } from './tenant-initialisation.service'; @@ -15,7 +14,6 @@ import { TenantInitialisationService } from './tenant-initialisation.service'; TenantMigrationModule, MigrationRunnerModule, ObjectMetadataModule, - FieldMetadataModule, DataSourceMetadataModule, ], exports: [TenantInitialisationService], diff --git a/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts b/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts index e8f47543ae..48438670ac 100644 --- a/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts +++ b/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts @@ -3,15 +3,11 @@ import { Injectable } from '@nestjs/common'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; import { DataSourceService } from 'src/metadata/data-source/data-source.service'; -import { FieldMetadataService } from 'src/metadata/field-metadata/services/field-metadata.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; import { DataSourceMetadata } from 'src/metadata/data-source-metadata/data-source-metadata.entity'; -import { standardObjectsMetadata } from './standard-objects/standard-object-metadata'; -import { standardObjectsSeeds } from './standard-objects/standard-object-seeds'; +import { standardObjectsPrefillData } from './standard-objects-prefill-data/standard-objects-prefill-data'; @Injectable() export class TenantInitialisationService { @@ -20,7 +16,6 @@ export class TenantInitialisationService { private readonly tenantMigrationService: TenantMigrationService, private readonly migrationRunnerService: MigrationRunnerService, private readonly objectMetadataService: ObjectMetadataService, - private readonly fieldMetadataService: FieldMetadataService, private readonly dataSourceMetadataService: DataSourceMetadataService, ) {} @@ -49,7 +44,7 @@ export class TenantInitialisationService { workspaceId, ); - await this.createObjectsAndFieldsMetadata( + await this.objectMetadataService.createStandardObjectsAndFieldsMetadata( dataSourceMetadata.id, workspaceId, ); @@ -62,69 +57,22 @@ export class TenantInitialisationService { /** * - * Create all standard objects and fields metadata for a given workspace + * We are prefilling a few standard objects with data to make it easier for the user to get started. * - * @param dataSourceMetadataId + * @param dataSourceMetadata * @param workspaceId */ - public async createObjectsAndFieldsMetadata( - dataSourceMetadataId: string, - workspaceId: string, - ) { - const createdObjectMetadata = await this.objectMetadataService.createMany( - Object.values(standardObjectsMetadata).map((objectMetadata) => ({ - ...objectMetadata, - dataSourceId: dataSourceMetadataId, - fields: [], - workspaceId, - isCustom: false, - isActive: true, - })), - ); - - await this.fieldMetadataService.createMany( - createdObjectMetadata.flatMap((objectMetadata: ObjectMetadata) => - standardObjectsMetadata[objectMetadata.nameSingular].fields.map( - (field: FieldMetadata) => ({ - ...field, - objectId: objectMetadata.id, - dataSourceId: dataSourceMetadataId, - workspaceId, - isCustom: false, - isActive: true, - }), - ), - ), - ); - } - - public async prefillWorkspaceWithStandardObjects( + private async prefillWorkspaceWithStandardObjects( dataSourceMetadata: DataSourceMetadata, workspaceId: string, ) { - const objects = - await this.objectMetadataService.getObjectMetadataFromDataSourceId( - dataSourceMetadata.id, - ); - const workspaceDataSource = await this.dataSourceService.connectToWorkspaceDataSource(workspaceId); - for (const object of objects) { - const seedData = standardObjectsSeeds[object.nameSingular]; - - if (!seedData) { - continue; - } - - const columns = Object.keys(seedData[0]); - - await workspaceDataSource - ?.createQueryBuilder() - .insert() - .into(`${dataSourceMetadata.schema}.${object.targetTableName}`, columns) - .values(seedData) - .execute(); + if (!workspaceDataSource) { + throw new Error('Could not connect to workspace data source'); } + + standardObjectsPrefillData(workspaceDataSource, dataSourceMetadata.schema); } } diff --git a/server/src/tenant/entity-resolver/utils/convert-fields-to-graphql.util.ts b/server/src/tenant/entity-resolver/utils/convert-fields-to-graphql.util.ts index 91a35e89e2..ce1647205a 100644 --- a/server/src/tenant/entity-resolver/utils/convert-fields-to-graphql.util.ts +++ b/server/src/tenant/entity-resolver/utils/convert-fields-to-graphql.util.ts @@ -15,7 +15,8 @@ export const convertFieldsToGraphQL = ( let fieldAlias = key; if (fieldsMap.has(key)) { - const metadata = fieldsMap.get(key); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const metadata = fieldsMap.get(key)!; if (!metadata) { throw new Error(`Field ${key} not found in fieldsMap`);