Fix seeds for local workspace and newly created workspaces (#2333)

* Update metadata/data seeds

* fix

* fix

* move seeding into a transaction

* add no-non-null-assertion

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
Weiko 2023-11-03 14:33:45 +01:00 committed by GitHub
parent 56a5f99108
commit b56f6f3947
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1392 additions and 630 deletions

View File

@ -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"
},

View File

@ -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"
}
]

View File

@ -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();
};

View File

@ -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();
};

View File

@ -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();
};

View File

@ -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();
};

View File

@ -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();
};

View File

@ -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<void> {
async run(): Promise<void> {
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;
}
}

View File

@ -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,

View File

@ -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,
);

View File

@ -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;

View File

@ -22,7 +22,7 @@ const configService = new ConfigService();
export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
url: configService.get<string>('PG_DATABASE_URL'),
type: 'postgres',
logging: false,
logging: ['query', 'error'],
schema: 'metadata',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,

View File

@ -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<ObjectMetadata> {
@ -84,4 +85,32 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadata> {
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,
})),
})),
);
}
}

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();
});
};

View File

@ -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
}
]
}

View File

@ -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
}
]

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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
}
]
}

View File

@ -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
}
]

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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"
}
]

View File

@ -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],

View File

@ -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);
}
}

View File

@ -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`);