Fix/metadata object and settings post merge (#2269)
* WIP * WIP2 * Seed views standard objects * Migrate views to the new data model --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -12,51 +12,4 @@ export const seedMetadata = async (prisma: PrismaClient) => {
|
||||
'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', 'workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd', 'postgres', 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'
|
||||
) ON CONFLICT DO NOTHING`,
|
||||
);
|
||||
|
||||
await prisma.$queryRawUnsafe(`CREATE TABLE IF NOT EXISTS
|
||||
workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd.company(
|
||||
"id" TEXT PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
"deletedAt" TIMESTAMP WITH TIME ZONE,
|
||||
"domainName" TEXT NOT NULL,
|
||||
"address" TEXT NOT NULL,
|
||||
"employees" INTEGER NOT NULL
|
||||
);
|
||||
`);
|
||||
await prisma.$queryRawUnsafe(`INSERT INTO workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd.company(
|
||||
"id", "name", "domainName", "address", "employees"
|
||||
)
|
||||
VALUES (
|
||||
'89bb825c-171e-4bcc-9cf7-43448d6fb278', 'Airbnb', 'airbnb.com', 'San Francisco', 5000
|
||||
), (
|
||||
'04b2e9f5-0713-40a5-8216-82802401d33e', 'Qonto', 'qonto.com', 'San Francisco', 800
|
||||
), (
|
||||
'118995f3-5d81-46d6-bf83-f7fd33ea6102', 'Facebook', 'facebook.com', 'San Francisco', 8000
|
||||
), (
|
||||
'460b6fb1-ed89-413a-b31a-962986e67bb4', 'Microsoft', 'microsoft.com', 'San Francisco', 800
|
||||
), (
|
||||
'fe256b39-3ec3-4fe3-8997-b76aa0bfa408', 'Linkedin', 'linkedin.com', 'San Francisco', 400
|
||||
) ON CONFLICT DO NOTHING`);
|
||||
|
||||
await prisma.$queryRawUnsafe(`INSERT INTO metadata.object_metadata(
|
||||
id, name_singular, name_plural, label_singular, label_plural, description, icon, target_table_name, is_custom, is_active, workspace_id, data_source_id
|
||||
)
|
||||
VALUES (
|
||||
'ba391617-ee08-432f-9438-2e17df5ac279', 'companyV2', 'companiesV2', 'Company', 'Companies', 'Companies', 'IconBuilding', 'company', false, true, 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1'
|
||||
) ON CONFLICT DO NOTHING`);
|
||||
|
||||
await prisma.$queryRawUnsafe(`INSERT INTO metadata.field_metadata(
|
||||
id, object_id, type, name, label, target_column_map, description, icon, enums, is_custom, is_active, is_nullable, workspace_id
|
||||
)
|
||||
VALUES (
|
||||
'22f5906d-153f-448c-b254-28adce721dcd', 'ba391617-ee08-432f-9438-2e17df5ac279', 'text', 'name', 'Name', '{"value": "name"}', 'Name', 'IconUser', NULL, false, true, false, 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'
|
||||
), (
|
||||
'19bfab29-1cbb-4ce2-9117-8540ac45a0f1', 'ba391617-ee08-432f-9438-2e17df5ac279', 'text', 'domainName', 'Domain Name', '{"value": "domainName"}', 'Domain Name', 'IconExternalLink', NULL, false, true, true, 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'
|
||||
), (
|
||||
'70130f27-9497-4b44-a04c-1a0fb9a4829c', 'ba391617-ee08-432f-9438-2e17df5ac279', 'text', 'address', 'Address', '{"value": "address"}', 'Address', 'IconMap', NULL, false, true, true, 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'
|
||||
), (
|
||||
'2a63c30e-8e80-475b-b5d7-9dda17adc537', 'ba391617-ee08-432f-9438-2e17df5ac279', 'number', 'employees', 'Employees', '{"value": "employees"}', 'Employees', 'IconUsers', NULL, false, true, true, 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'
|
||||
) ON CONFLICT DO NOTHING`);
|
||||
};
|
||||
|
||||
47
server/src/metadata/commands/data-seed-tenant.command.ts
Normal file
47
server/src/metadata/commands/data-seed-tenant.command.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { TenantInitialisationService } from '../tenant-initialisation/tenant-initialisation.service';
|
||||
import { DataSourceMetadataService } from '../data-source-metadata/data-source-metadata.service';
|
||||
|
||||
// TODO: implement dry-run
|
||||
interface DataSeedTenantOptions {
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: 'tenant:data-seed',
|
||||
description: 'Seed tenant with initial data',
|
||||
})
|
||||
export class DataSeedTenantCommand extends CommandRunner {
|
||||
constructor(
|
||||
private readonly dataSourceMetadataService: DataSourceMetadataService,
|
||||
private readonly tenantInitialisationService: TenantInitialisationService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(
|
||||
_passedParam: string[],
|
||||
options: DataSeedTenantOptions,
|
||||
): Promise<void> {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
options.workspaceId,
|
||||
);
|
||||
// TODO: run in a dedicated job + run queries in a transaction.
|
||||
await this.tenantInitialisationService.prefillWorkspaceWithStandardObjects(
|
||||
dataSourceMetadata,
|
||||
options.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;
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data
|
||||
|
||||
import { SyncTenantMetadataCommand } from './sync-tenant-metadata.command';
|
||||
import { RunTenantMigrationsCommand } from './run-tenant-migrations.command';
|
||||
import { DataSeedTenantCommand } from './data-seed-tenant.command';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -19,6 +20,10 @@ import { RunTenantMigrationsCommand } from './run-tenant-migrations.command';
|
||||
DataSourceMetadataModule,
|
||||
TenantInitialisationModule,
|
||||
],
|
||||
providers: [RunTenantMigrationsCommand, SyncTenantMetadataCommand],
|
||||
providers: [
|
||||
RunTenantMigrationsCommand,
|
||||
SyncTenantMetadataCommand,
|
||||
DataSeedTenantCommand,
|
||||
],
|
||||
})
|
||||
export class MetadataCommandModule {}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service';
|
||||
import { FieldMetadataService } from 'src/metadata/field-metadata/services/field-metadata.service';
|
||||
import { TenantInitialisationService } from 'src/metadata/tenant-initialisation/tenant-initialisation.service';
|
||||
import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service';
|
||||
|
||||
@ -17,7 +16,6 @@ interface RunTenantMigrationsOptions {
|
||||
export class SyncTenantMetadataCommand extends CommandRunner {
|
||||
constructor(
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly fieldMetadataService: FieldMetadataService,
|
||||
private readonly dataSourceMetadataService: DataSourceMetadataService,
|
||||
private readonly tenantInitialisationService: TenantInitialisationService,
|
||||
) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"id": "1a8487a0-480c-434e-b4c7-e22408b97047",
|
||||
"nameSingular": "companyV2",
|
||||
"namePlural": "companiesV2",
|
||||
"labelSingular": "Company",
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import companyObject from './companies/companies.metadata.json';
|
||||
import personObject from './people/people.metadata.json';
|
||||
import viewObject from './views/views.metadata.json';
|
||||
import viewFieldObject from './view-fields/view-fields.metadata.json';
|
||||
|
||||
export const standardObjectsMetadata = {
|
||||
companyV2: companyObject,
|
||||
personV2: personObject,
|
||||
viewV2: viewObject,
|
||||
viewFieldV2: viewFieldObject,
|
||||
};
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
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,
|
||||
};
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
{
|
||||
"nameSingular": "viewFieldV2",
|
||||
"namePlural": "viewFieldsV2",
|
||||
"labelSingular": "View Field",
|
||||
"labelPlural": "View Fields",
|
||||
"targetTableName": "viewField",
|
||||
"description": "(System) View Fields",
|
||||
"icon": "arrows-sort",
|
||||
"fields": [
|
||||
{
|
||||
"type": "text",
|
||||
"name": "objectId",
|
||||
"label": "Object Id",
|
||||
"targetColumnMap": {
|
||||
"value": "objectId"
|
||||
},
|
||||
"description": "View Field target object",
|
||||
"icon": null,
|
||||
"isNullable": false
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
[
|
||||
{
|
||||
"objectId": "1a8487a0-480c-434e-b4c7-e22408b97047",
|
||||
"fieldId": "name",
|
||||
"viewId": "10bec73c-0aea-4cc4-a3b2-8c2186f29b43",
|
||||
"position": 0,
|
||||
"isVisible": true,
|
||||
"size": 180
|
||||
},
|
||||
|
||||
{
|
||||
"objectId": "company",
|
||||
"fieldId": "name",
|
||||
"viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80",
|
||||
"position": 0,
|
||||
"isVisible": true,
|
||||
"size": 180
|
||||
},
|
||||
{
|
||||
"objectId": "company",
|
||||
"fieldId": "domainName",
|
||||
"viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80",
|
||||
"position": 1,
|
||||
"isVisible": true,
|
||||
"size": 100
|
||||
},
|
||||
{
|
||||
"objectId": "company",
|
||||
"fieldId": "accountOwner",
|
||||
"viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80",
|
||||
"position": 2,
|
||||
"isVisible": true,
|
||||
"size": 150
|
||||
},
|
||||
{
|
||||
"objectId": "company",
|
||||
"fieldId": "createdAt",
|
||||
"viewId": "37a8a866-eb17-4e76-9382-03143a2f6a80",
|
||||
"position": 3,
|
||||
"isVisible": true,
|
||||
"size": 150
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,44 @@
|
||||
{
|
||||
"nameSingular": "viewV2",
|
||||
"namePlural": "viewsV2",
|
||||
"labelSingular": "View",
|
||||
"labelPlural": "Views",
|
||||
"targetTableName": "view",
|
||||
"description": "(System) Views",
|
||||
"icon": "layout-collage",
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"id": "37a8a866-eb17-4e76-9382-03143a2f6a80",
|
||||
"name": "All companies",
|
||||
"objectId": "company",
|
||||
"type": "Table"
|
||||
},
|
||||
{
|
||||
"id": "6095799e-b48f-4e00-b071-10818083593a",
|
||||
"name": "All companies",
|
||||
"objectId": "person",
|
||||
"type": "Table"
|
||||
},
|
||||
{
|
||||
"id": "e26f66b7-f890-4a5c-b4d2-ec09987b5308",
|
||||
"name": "All Opportunities",
|
||||
"objectId": "company",
|
||||
"type": "Pipeline"
|
||||
},
|
||||
{
|
||||
"id": "10bec73c-0aea-4cc4-a3b2-8c2186f29b43",
|
||||
"name": "All Companies (V2)",
|
||||
"objectId": "1a8487a0-480c-434e-b4c7-e22408b97047",
|
||||
"type": "Table"
|
||||
}
|
||||
]
|
||||
@ -98,7 +98,7 @@ export class TenantInitialisationService {
|
||||
);
|
||||
}
|
||||
|
||||
private async prefillWorkspaceWithStandardObjects(
|
||||
public async prefillWorkspaceWithStandardObjects(
|
||||
dataSourceMetadata: DataSourceMetadata,
|
||||
workspaceId: string,
|
||||
) {
|
||||
@ -117,11 +117,7 @@ export class TenantInitialisationService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fields = standardObjectsMetadata[object.nameSingular].fields;
|
||||
|
||||
const columns = fields.map((field: FieldMetadata) =>
|
||||
Object.values(field.targetColumnMap),
|
||||
);
|
||||
const columns = Object.keys(seedData[0]);
|
||||
|
||||
await workspaceDataSource
|
||||
?.createQueryBuilder()
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity';
|
||||
|
||||
export const addViewTable: TenantMigrationTableAction[] = [
|
||||
{
|
||||
name: 'view',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'view',
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'objectId',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,44 @@
|
||||
import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity';
|
||||
|
||||
export const addViewFieldTable: TenantMigrationTableAction[] = [
|
||||
{
|
||||
name: 'viewField',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'viewField',
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
name: 'objectId',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'viewId',
|
||||
type: 'varchar',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
type: 'integer',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'isVisible',
|
||||
type: 'boolean',
|
||||
action: 'create',
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
type: 'integer',
|
||||
action: 'create',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@ -1,8 +1,12 @@
|
||||
import { addCompanyTable } from './migrations/1697618009-addCompanyTable';
|
||||
import { addPeopleTable } from './migrations/1697618010-addPeopleTable';
|
||||
import { addViewTable } from './migrations/1697618011-addViewTable';
|
||||
import { addViewFieldTable } from './migrations/1697618012-addViewFieldTable';
|
||||
|
||||
// TODO: read the folder and return all migrations
|
||||
export const standardMigrations = {
|
||||
'1697618009-addCompanyTable': addCompanyTable,
|
||||
'1697618010-addPeopleTable': addPeopleTable,
|
||||
'1697618011-addViewTable': addViewTable,
|
||||
'1697618012-addViewFieldTable': addViewFieldTable,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user