Sync metadata generate migrations (#2864)
* Sync Metadata generates migrations * add execute migrations * fix relations + add isActive on creation * fix composite fields migration * remove dependency * use new metadata setup for seed-dev * fix rebase * remove unused code * fix viewField dev seeds * fix isSystem
This commit is contained in:
@ -4,14 +4,14 @@ import { DatabaseCommandModule } from 'src/database/commands/database-command.mo
|
|||||||
|
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
import { WorkspaceManagerCommandsModule } from './workspace/workspace-manager/commands/workspace-manager-commands.module';
|
import { WorkspaceSyncMetadataCommandsModule } from './workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module';
|
||||||
import { WorkspaceMigrationRunnerCommandsModule } from './workspace/workspace-migration-runner/commands/workspace-migration-runner-commands.module';
|
import { WorkspaceMigrationRunnerCommandsModule } from './workspace/workspace-migration-runner/commands/workspace-migration-runner-commands.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
AppModule,
|
AppModule,
|
||||||
WorkspaceMigrationRunnerCommandsModule,
|
WorkspaceMigrationRunnerCommandsModule,
|
||||||
WorkspaceManagerCommandsModule,
|
WorkspaceSyncMetadataCommandsModule,
|
||||||
DatabaseCommandModule,
|
DatabaseCommandModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,19 +2,18 @@ import { Command, CommandRunner } from 'nest-commander';
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
|
|
||||||
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
|
|
||||||
import { seedCompanies } from 'src/database/typeorm-seeds/workspace/companies';
|
import { seedCompanies } from 'src/database/typeorm-seeds/workspace/companies';
|
||||||
import { seedViewFields } from 'src/database/typeorm-seeds/workspace/view-fields';
|
|
||||||
import { seedViews } from 'src/database/typeorm-seeds/workspace/views';
|
import { seedViews } from 'src/database/typeorm-seeds/workspace/views';
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import { seedMetadataSchema } from 'src/database/typeorm-seeds/metadata';
|
|
||||||
import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunity';
|
import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunity';
|
||||||
import { seedPipelineStep } from 'src/database/typeorm-seeds/workspace/pipeline-step';
|
import { seedPipelineStep } from 'src/database/typeorm-seeds/workspace/pipeline-step';
|
||||||
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspaceMember';
|
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspaceMember';
|
||||||
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
|
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
|
||||||
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
|
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
|
||||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||||
|
import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync.metadata.service';
|
||||||
|
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||||
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
|
|
||||||
// TODO: implement dry-run
|
// TODO: implement dry-run
|
||||||
@Command({
|
@Command({
|
||||||
@ -29,8 +28,9 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
private readonly environmentService: EnvironmentService,
|
private readonly environmentService: EnvironmentService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
private readonly typeORMService: TypeORMService,
|
private readonly typeORMService: TypeORMService,
|
||||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
|
||||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||||
|
private readonly objectMetadataService: ObjectMetadataService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -41,13 +41,30 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
url: this.environmentService.getPGDatabaseUrl(),
|
url: this.environmentService.getPGDatabaseUrl(),
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
logging: true,
|
logging: true,
|
||||||
schema: 'public',
|
schema: 'core',
|
||||||
});
|
});
|
||||||
|
|
||||||
await dataSource.initialize();
|
await dataSource.initialize();
|
||||||
|
|
||||||
await seedCoreSchema(dataSource, this.workspaceId);
|
await seedCoreSchema(dataSource, this.workspaceId);
|
||||||
await seedMetadataSchema(dataSource);
|
|
||||||
|
await dataSource.destroy();
|
||||||
|
|
||||||
|
const schemaName = await this.workspaceDataSourceService.createWorkspaceDBSchema(
|
||||||
|
this.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataSourceMetadata =
|
||||||
|
await this.dataSourceService.createDataSourceMetadata(
|
||||||
|
this.workspaceId,
|
||||||
|
schemaName,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata(
|
||||||
|
dataSourceMetadata.id,
|
||||||
|
this.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
@ -68,20 +85,27 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.workspaceMigrationService.insertStandardMigrations(
|
const objectMetadata = await this.objectMetadataService.findManyWithinWorkspace(this.workspaceId);
|
||||||
this.workspaceId,
|
const objectMetadataMap = objectMetadata.reduce((acc, object) => {
|
||||||
);
|
acc[object.nameSingular] = {
|
||||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
id: object.id,
|
||||||
this.workspaceId,
|
fields: object.fields.reduce((acc, field) => {
|
||||||
);
|
acc[field.name] = field.id;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
|
||||||
await seedCompanies(workspaceDataSource, dataSourceMetadata.schema);
|
await seedCompanies(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
await seedPeople(workspaceDataSource, dataSourceMetadata.schema);
|
await seedPeople(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
await seedPipelineStep(workspaceDataSource, dataSourceMetadata.schema);
|
await seedPipelineStep(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
await seedOpportunity(workspaceDataSource, dataSourceMetadata.schema);
|
await seedOpportunity(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
|
|
||||||
await seedViews(workspaceDataSource, dataSourceMetadata.schema);
|
await seedViews(workspaceDataSource, dataSourceMetadata.schema, objectMetadataMap);
|
||||||
await seedViewFields(workspaceDataSource, dataSourceMetadata.schema);
|
|
||||||
await seedWorkspaceMember(workspaceDataSource, dataSourceMetadata.schema);
|
await seedWorkspaceMember(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@ -9,15 +9,19 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
|||||||
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
|
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
|
||||||
import { DataSeedWorkspaceCommand } from 'src/database/commands/data-seed-dev-workspace.command';
|
import { DataSeedWorkspaceCommand } from 'src/database/commands/data-seed-dev-workspace.command';
|
||||||
import { DataSeedDemoWorkspaceCommand } from 'src/database/commands/data-seed-demo-workspace.command';
|
import { DataSeedDemoWorkspaceCommand } from 'src/database/commands/data-seed-demo-workspace.command';
|
||||||
|
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||||
|
import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module';
|
||||||
|
import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
WorkspaceManagerModule,
|
WorkspaceManagerModule,
|
||||||
DataSourceModule,
|
DataSourceModule,
|
||||||
TypeORMModule,
|
TypeORMModule,
|
||||||
WorkspaceMigrationModule,
|
|
||||||
WorkspaceMigrationRunnerModule,
|
|
||||||
WorkspaceModule,
|
WorkspaceModule,
|
||||||
|
WorkspaceDataSourceModule,
|
||||||
|
WorkspaceSyncMetadataModule,
|
||||||
|
ObjectMetadataModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
DataSeedWorkspaceCommand,
|
DataSeedWorkspaceCommand,
|
||||||
|
|||||||
@ -124,19 +124,19 @@ export const seedViewFilterFieldMetadata = async (
|
|||||||
isCustom: false,
|
isCustom: false,
|
||||||
workspaceId: SeedWorkspaceId,
|
workspaceId: SeedWorkspaceId,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
type: FieldMetadataType.UUID,
|
type: FieldMetadataType.RELATION,
|
||||||
name: 'viewId',
|
name: 'view',
|
||||||
label: 'View Id',
|
label: 'View',
|
||||||
targetColumnMap: {},
|
targetColumnMap: {},
|
||||||
description: 'View Filter related view',
|
description: 'View Filter related view',
|
||||||
icon: 'IconLayoutCollage',
|
icon: 'IconLayoutCollage',
|
||||||
isNullable: false,
|
isNullable: true,
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
defaultValue: undefined,
|
defaultValue: undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: SeedViewFilterFieldMetadataIds.ViewForeignKey,
|
id: SeedViewFilterFieldMetadataIds.ViewForeignKey,
|
||||||
objectMetadataId: SeedObjectMetadataIds.ViewField,
|
objectMetadataId: SeedObjectMetadataIds.ViewFilter,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
workspaceId: SeedWorkspaceId,
|
workspaceId: SeedWorkspaceId,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
|
|||||||
@ -1,169 +0,0 @@
|
|||||||
import { DataSource } from 'typeorm';
|
|
||||||
|
|
||||||
import { SeedViewIds } from 'src/database/typeorm-seeds/workspace/views';
|
|
||||||
import { SeedCompanyFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/company';
|
|
||||||
import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person';
|
|
||||||
import { SeedOpportunityFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/opportunity';
|
|
||||||
|
|
||||||
const tableName = 'viewField';
|
|
||||||
|
|
||||||
export const seedViewFields = async (
|
|
||||||
workspaceDataSource: DataSource,
|
|
||||||
schemaName: string,
|
|
||||||
) => {
|
|
||||||
await workspaceDataSource
|
|
||||||
.createQueryBuilder()
|
|
||||||
.insert()
|
|
||||||
.into(`${schemaName}.${tableName}`, [
|
|
||||||
'fieldMetadataId',
|
|
||||||
'viewId',
|
|
||||||
'position',
|
|
||||||
'isVisible',
|
|
||||||
'size',
|
|
||||||
])
|
|
||||||
.orIgnore()
|
|
||||||
.values([
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Name,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 0,
|
|
||||||
isVisible: true,
|
|
||||||
size: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.DomainName,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 1,
|
|
||||||
isVisible: true,
|
|
||||||
size: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.AccountOwner,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 2,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.CreatedAt,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 3,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Employees,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 4,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.LinkedinLink,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 5,
|
|
||||||
isVisible: true,
|
|
||||||
size: 170,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Address,
|
|
||||||
viewId: SeedViewIds.Company,
|
|
||||||
position: 6,
|
|
||||||
isVisible: true,
|
|
||||||
size: 170,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.Name,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 0,
|
|
||||||
isVisible: true,
|
|
||||||
size: 210,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.Email,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 1,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.Company,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 2,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.Phone,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 3,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.CreatedAt,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 4,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.City,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 5,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.JobTitle,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 6,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.LinkedinLink,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 7,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedPersonFieldMetadataIds.XLink,
|
|
||||||
viewId: SeedViewIds.Person,
|
|
||||||
position: 8,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.Amount,
|
|
||||||
viewId: SeedViewIds.Opportunity,
|
|
||||||
position: 0,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.CloseDate,
|
|
||||||
viewId: SeedViewIds.Opportunity,
|
|
||||||
position: 1,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.Probability,
|
|
||||||
viewId: SeedViewIds.Opportunity,
|
|
||||||
position: 2,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.PointOfContact,
|
|
||||||
viewId: SeedViewIds.Opportunity,
|
|
||||||
position: 3,
|
|
||||||
isVisible: true,
|
|
||||||
size: 150,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
};
|
|
||||||
@ -1,48 +1,198 @@
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/object-metadata';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
|
|
||||||
const tableName = 'view';
|
|
||||||
|
|
||||||
export const enum SeedViewIds {
|
|
||||||
Company = '20202020-2441-4424-8163-4002c523d415',
|
|
||||||
Person = '20202020-1979-447d-8115-593744eb4ead',
|
|
||||||
Opportunity = '20202020-b2b3-48a5-96ce-0936d6af21f7',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const seedViews = async (
|
export const seedViews = async (
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
|
objectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||||
) => {
|
) => {
|
||||||
|
const createdViews = await workspaceDataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(`${schemaName}.view`, [
|
||||||
|
'name',
|
||||||
|
'objectMetadataId',
|
||||||
|
'type',
|
||||||
|
])
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
name: 'All Companies',
|
||||||
|
objectMetadataId: objectMetadataMap['company'].id,
|
||||||
|
type: 'table',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'All People',
|
||||||
|
objectMetadataId: objectMetadataMap['person'].id,
|
||||||
|
type: 'table',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'All Opportunities',
|
||||||
|
objectMetadataId: objectMetadataMap['opportunity'].id,
|
||||||
|
type: 'kanban',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.returning('*')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
const viewIdMap = createdViews.raw.reduce((acc, view) => {
|
||||||
|
acc[view.name] = view.id;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
await workspaceDataSource
|
await workspaceDataSource
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.insert()
|
.insert()
|
||||||
.into(`${schemaName}.${tableName}`, [
|
.into(`${schemaName}.viewField`, [
|
||||||
'id',
|
'fieldMetadataId',
|
||||||
'name',
|
'viewId',
|
||||||
'objectMetadataId',
|
'position',
|
||||||
'type',
|
'isVisible',
|
||||||
])
|
'size',
|
||||||
.orIgnore()
|
])
|
||||||
.values([
|
.values([
|
||||||
{
|
{
|
||||||
id: SeedViewIds.Company,
|
fieldMetadataId: objectMetadataMap['company'].fields['name'],
|
||||||
name: 'All Companies',
|
viewId: viewIdMap['All Companies'],
|
||||||
objectMetadataId: SeedObjectMetadataIds.Company,
|
position: 0,
|
||||||
type: 'table',
|
isVisible: true,
|
||||||
},
|
size: 180,
|
||||||
{
|
},
|
||||||
id: SeedViewIds.Person,
|
{
|
||||||
name: 'All People',
|
fieldMetadataId: objectMetadataMap['company'].fields['domainName'],
|
||||||
objectMetadataId: SeedObjectMetadataIds.Person,
|
viewId: viewIdMap['All Companies'],
|
||||||
type: 'table',
|
position: 1,
|
||||||
},
|
isVisible: true,
|
||||||
{
|
size: 100,
|
||||||
id: SeedViewIds.Opportunity,
|
},
|
||||||
name: 'All Opportunities',
|
{
|
||||||
objectMetadataId: SeedObjectMetadataIds.Opportunity,
|
fieldMetadataId: objectMetadataMap['company'].fields['accountOwner'],
|
||||||
type: 'kanban',
|
viewId: viewIdMap['All Companies'],
|
||||||
},
|
position: 2,
|
||||||
])
|
isVisible: true,
|
||||||
.execute();
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['company'].fields['createdAt'],
|
||||||
|
viewId: viewIdMap['All Companies'],
|
||||||
|
position: 3,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['company'].fields['employees'],
|
||||||
|
viewId: viewIdMap['All Companies'],
|
||||||
|
position: 4,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['company'].fields['linkedinLink'],
|
||||||
|
viewId: viewIdMap['All Companies'],
|
||||||
|
position: 5,
|
||||||
|
isVisible: true,
|
||||||
|
size: 170,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['company'].fields['address'],
|
||||||
|
viewId: viewIdMap['All Companies'],
|
||||||
|
position: 6,
|
||||||
|
isVisible: true,
|
||||||
|
size: 170,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['name'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 0,
|
||||||
|
isVisible: true,
|
||||||
|
size: 210,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['email'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 1,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['company'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 2,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['phone'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 3,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['createdAt'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 4,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['city'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 5,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['jobTitle'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 6,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['linkedinLink'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 7,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['person'].fields['xLink'],
|
||||||
|
viewId: viewIdMap['All People'],
|
||||||
|
position: 8,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['opportunity'].fields['amount'],
|
||||||
|
viewId: viewIdMap['All Opportunities'],
|
||||||
|
position: 0,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['opportunity'].fields['closeDate'],
|
||||||
|
viewId: viewIdMap['All Opportunities'],
|
||||||
|
position: 1,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['opportunity'].fields['probability'],
|
||||||
|
viewId: viewIdMap['All Opportunities'],
|
||||||
|
position: 2,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldMetadataId: objectMetadataMap['opportunity'].fields['pointOfContact'],
|
||||||
|
viewId: viewIdMap['All Opportunities'],
|
||||||
|
position: 3,
|
||||||
|
isVisible: true,
|
||||||
|
size: 150,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
|||||||
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { typeORMCoreModuleOptions } from 'src/database/typeorm/core/core.datasource';
|
import { typeORMCoreModuleOptions } from 'src/database/typeorm/core/core.datasource';
|
||||||
|
import { EnvironmentModule } from 'src/integrations/environment/environment.module';
|
||||||
|
|
||||||
import { TypeORMService } from './typeorm.service';
|
import { TypeORMService } from './typeorm.service';
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ const coreTypeORMFactory = async (): Promise<TypeOrmModuleOptions> => ({
|
|||||||
useFactory: coreTypeORMFactory,
|
useFactory: coreTypeORMFactory,
|
||||||
name: 'core',
|
name: 'core',
|
||||||
}),
|
}),
|
||||||
|
EnvironmentModule,
|
||||||
],
|
],
|
||||||
providers: [TypeORMService],
|
providers: [TypeORMService],
|
||||||
exports: [TypeORMService],
|
exports: [TypeORMService],
|
||||||
|
|||||||
@ -3,21 +3,21 @@ import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/f
|
|||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
export const currencyObjectDefinition = {
|
export const currencyFields = (
|
||||||
id: FieldMetadataType.CURRENCY.toString(),
|
fieldMetadata?: FieldMetadataInterface,
|
||||||
nameSingular: 'currency',
|
): FieldMetadataInterface[] => {
|
||||||
namePlural: 'currency',
|
return [
|
||||||
labelSingular: 'Currency',
|
|
||||||
labelPlural: 'Currency',
|
|
||||||
targetTableName: '',
|
|
||||||
fields: [
|
|
||||||
{
|
{
|
||||||
id: 'amountMicros',
|
id: 'amountMicros',
|
||||||
type: FieldMetadataType.NUMERIC,
|
type: FieldMetadataType.NUMERIC,
|
||||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||||
name: 'amountMicros',
|
name: 'amountMicros',
|
||||||
label: 'AmountMicros',
|
label: 'AmountMicros',
|
||||||
targetColumnMap: { value: 'amountMicros' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata
|
||||||
|
? `${fieldMetadata.name}AmountMicros`
|
||||||
|
: 'amountMicros',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
{
|
{
|
||||||
@ -26,10 +26,24 @@ export const currencyObjectDefinition = {
|
|||||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||||
name: 'currencyCode',
|
name: 'currencyCode',
|
||||||
label: 'Currency Code',
|
label: 'Currency Code',
|
||||||
targetColumnMap: { value: 'currencyCode' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata
|
||||||
|
? `${fieldMetadata.name}CurrencyCode`
|
||||||
|
: 'currencyCode',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
],
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const currencyObjectDefinition = {
|
||||||
|
id: FieldMetadataType.CURRENCY.toString(),
|
||||||
|
nameSingular: 'currency',
|
||||||
|
namePlural: 'currency',
|
||||||
|
labelSingular: 'Currency',
|
||||||
|
labelPlural: 'Currency',
|
||||||
|
targetTableName: '',
|
||||||
|
fields: currencyFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|||||||
@ -3,21 +3,19 @@ import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/f
|
|||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
export const fullNameObjectDefinition = {
|
export const fullNameFields = (
|
||||||
id: FieldMetadataType.FULL_NAME.toString(),
|
fieldMetadata?: FieldMetadataInterface,
|
||||||
nameSingular: 'fullName',
|
): FieldMetadataInterface[] => {
|
||||||
namePlural: 'fullName',
|
return [
|
||||||
labelSingular: 'FullName',
|
|
||||||
labelPlural: 'FullName',
|
|
||||||
targetTableName: '',
|
|
||||||
fields: [
|
|
||||||
{
|
{
|
||||||
id: 'firstName',
|
id: 'firstName',
|
||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||||
name: 'firstName',
|
name: 'firstName',
|
||||||
label: 'First Name',
|
label: 'First Name',
|
||||||
targetColumnMap: { value: 'firstName' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata ? `${fieldMetadata.name}FirstName` : 'firstName',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
{
|
{
|
||||||
@ -26,10 +24,22 @@ export const fullNameObjectDefinition = {
|
|||||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||||
name: 'lastName',
|
name: 'lastName',
|
||||||
label: 'Last Name',
|
label: 'Last Name',
|
||||||
targetColumnMap: { value: 'lastName' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata ? `${fieldMetadata.name}LastName` : 'lastName',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
],
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fullNameObjectDefinition = {
|
||||||
|
id: FieldMetadataType.FULL_NAME.toString(),
|
||||||
|
nameSingular: 'fullName',
|
||||||
|
namePlural: 'fullName',
|
||||||
|
labelSingular: 'FullName',
|
||||||
|
labelPlural: 'FullName',
|
||||||
|
targetTableName: '',
|
||||||
|
fields: fullNameFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|||||||
@ -3,21 +3,19 @@ import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/f
|
|||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
export const linkObjectDefinition = {
|
export const linkFields = (
|
||||||
id: FieldMetadataType.LINK.toString(),
|
fieldMetadata?: FieldMetadataInterface,
|
||||||
nameSingular: 'link',
|
): FieldMetadataInterface[] => {
|
||||||
namePlural: 'link',
|
return [
|
||||||
labelSingular: 'Link',
|
|
||||||
labelPlural: 'Link',
|
|
||||||
targetTableName: '',
|
|
||||||
fields: [
|
|
||||||
{
|
{
|
||||||
id: 'label',
|
id: 'label',
|
||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||||
name: 'label',
|
name: 'label',
|
||||||
label: 'Label',
|
label: 'Label',
|
||||||
targetColumnMap: { value: 'label' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata ? `${fieldMetadata.name}Label` : 'label',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
{
|
{
|
||||||
@ -26,10 +24,22 @@ export const linkObjectDefinition = {
|
|||||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||||
name: 'url',
|
name: 'url',
|
||||||
label: 'Url',
|
label: 'Url',
|
||||||
targetColumnMap: { value: 'url' },
|
targetColumnMap: {
|
||||||
|
value: fieldMetadata ? `${fieldMetadata.name}Url` : 'url',
|
||||||
|
},
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies FieldMetadataInterface,
|
} satisfies FieldMetadataInterface,
|
||||||
],
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const linkObjectDefinition = {
|
||||||
|
id: FieldMetadataType.LINK.toString(),
|
||||||
|
nameSingular: 'link',
|
||||||
|
namePlural: 'link',
|
||||||
|
labelSingular: 'Link',
|
||||||
|
labelPlural: 'Link',
|
||||||
|
targetTableName: '',
|
||||||
|
fields: linkFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export function generateDefaultValue(
|
||||||
|
type: FieldMetadataType,
|
||||||
|
): FieldMetadataDefaultValue {
|
||||||
|
switch (type) {
|
||||||
|
case FieldMetadataType.TEXT:
|
||||||
|
case FieldMetadataType.PHONE:
|
||||||
|
case FieldMetadataType.EMAIL:
|
||||||
|
return {
|
||||||
|
value: '',
|
||||||
|
};
|
||||||
|
case FieldMetadataType.FULL_NAME:
|
||||||
|
return {
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,9 +12,13 @@ import {
|
|||||||
WorkspaceMigrationColumnActionType,
|
WorkspaceMigrationColumnActionType,
|
||||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
import { linkObjectDefinition } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
import { fullNameFields } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
|
||||||
import { currencyObjectDefinition } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
|
import { currencyFields } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
|
||||||
import { fullNameObjectDefinition } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
|
import { linkFields } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
||||||
|
|
||||||
|
type CompositeFieldSplitterFunction = (
|
||||||
|
fieldMetadata: FieldMetadataInterface,
|
||||||
|
) => FieldMetadataInterface[];
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceMigrationFactory {
|
export class WorkspaceMigrationFactory {
|
||||||
@ -26,7 +30,10 @@ export class WorkspaceMigrationFactory {
|
|||||||
options?: WorkspaceColumnActionOptions;
|
options?: WorkspaceColumnActionOptions;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
private compositeDefinitions = new Map<string, FieldMetadataInterface[]>();
|
private compositeDefinitions = new Map<
|
||||||
|
string,
|
||||||
|
CompositeFieldSplitterFunction
|
||||||
|
>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly basicColumnActionFactory: BasicColumnActionFactory,
|
private readonly basicColumnActionFactory: BasicColumnActionFactory,
|
||||||
@ -83,11 +90,13 @@ export class WorkspaceMigrationFactory {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.compositeDefinitions = new Map<string, FieldMetadataInterface[]>([
|
this.compositeDefinitions = new Map<string, CompositeFieldSplitterFunction>(
|
||||||
[FieldMetadataType.LINK, linkObjectDefinition.fields],
|
[
|
||||||
[FieldMetadataType.CURRENCY, currencyObjectDefinition.fields],
|
[FieldMetadataType.LINK, linkFields],
|
||||||
[FieldMetadataType.FULL_NAME, fullNameObjectDefinition.fields],
|
[FieldMetadataType.CURRENCY, currencyFields],
|
||||||
]);
|
[FieldMetadataType.FULL_NAME, fullNameFields],
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createColumnActions(
|
createColumnActions(
|
||||||
@ -128,11 +137,11 @@ export class WorkspaceMigrationFactory {
|
|||||||
|
|
||||||
// If it's a composite field type, we need to create a column action for each of the fields
|
// If it's a composite field type, we need to create a column action for each of the fields
|
||||||
if (isCompositeFieldMetadataType(alteredFieldMetadata.type)) {
|
if (isCompositeFieldMetadataType(alteredFieldMetadata.type)) {
|
||||||
const fieldMetadataCollection = this.compositeDefinitions.get(
|
const fieldMetadataSplitterFunction = this.compositeDefinitions.get(
|
||||||
alteredFieldMetadata.type,
|
alteredFieldMetadata.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!fieldMetadataCollection) {
|
if (!fieldMetadataSplitterFunction) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
||||||
{
|
{
|
||||||
@ -145,6 +154,9 @@ export class WorkspaceMigrationFactory {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fieldMetadataCollection =
|
||||||
|
fieldMetadataSplitterFunction(alteredFieldMetadata);
|
||||||
|
|
||||||
return fieldMetadataCollection.map((fieldMetadata) =>
|
return fieldMetadataCollection.map((fieldMetadata) =>
|
||||||
this.createColumnAction(action, fieldMetadata, fieldMetadata),
|
this.createColumnAction(action, fieldMetadata, fieldMetadata),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,103 +0,0 @@
|
|||||||
import camelCase from 'lodash.camelcase';
|
|
||||||
import 'reflect-metadata';
|
|
||||||
|
|
||||||
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
|
||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import { generateTargetColumnMap } from 'src/metadata/field-metadata/utils/generate-target-column-map.util';
|
|
||||||
|
|
||||||
export type FieldMetadataDecorator = {
|
|
||||||
type: FieldMetadataType;
|
|
||||||
label: string;
|
|
||||||
description?: string | null;
|
|
||||||
icon?: string | null;
|
|
||||||
defaultValue?: FieldMetadataDefaultValue | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ObjectMetadataDecorator = {
|
|
||||||
namePlural: string;
|
|
||||||
labelSingular: string;
|
|
||||||
labelPlural: string;
|
|
||||||
description?: string | null;
|
|
||||||
icon?: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const classSuffix = 'ObjectMetadata';
|
|
||||||
|
|
||||||
export function ObjectMetadata(
|
|
||||||
metadata: ObjectMetadataDecorator,
|
|
||||||
): ClassDecorator {
|
|
||||||
return (target) => {
|
|
||||||
const isSystem = Reflect.getMetadata('isSystem', target) || false;
|
|
||||||
|
|
||||||
let objectName = camelCase(target.name);
|
|
||||||
|
|
||||||
if (objectName.endsWith(classSuffix)) {
|
|
||||||
objectName = objectName.slice(0, -classSuffix.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reflect.defineMetadata(
|
|
||||||
'objectMetadata',
|
|
||||||
{
|
|
||||||
nameSingular: objectName,
|
|
||||||
...metadata,
|
|
||||||
targetTableName: objectName,
|
|
||||||
isSystem,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
},
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function IsNullable() {
|
|
||||||
return function (target: object, fieldKey: string) {
|
|
||||||
Reflect.defineMetadata('isNullable', true, target, fieldKey);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function IsSystem() {
|
|
||||||
return function (target: object, fieldKey?: string) {
|
|
||||||
if (fieldKey) {
|
|
||||||
Reflect.defineMetadata('isSystem', true, target, fieldKey);
|
|
||||||
} else {
|
|
||||||
Reflect.defineMetadata('isSystem', true, target);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FieldMetadata(
|
|
||||||
metadata: FieldMetadataDecorator,
|
|
||||||
): PropertyDecorator {
|
|
||||||
return (target: object, fieldKey: string) => {
|
|
||||||
const existingFieldMetadata =
|
|
||||||
Reflect.getMetadata('fieldMetadata', target.constructor) || {};
|
|
||||||
|
|
||||||
const isNullable =
|
|
||||||
Reflect.getMetadata('isNullable', target, fieldKey) || false;
|
|
||||||
|
|
||||||
const isSystem = Reflect.getMetadata('isSystem', target, fieldKey) || false;
|
|
||||||
|
|
||||||
Reflect.defineMetadata(
|
|
||||||
'fieldMetadata',
|
|
||||||
{
|
|
||||||
...existingFieldMetadata,
|
|
||||||
[fieldKey]: {
|
|
||||||
name: fieldKey,
|
|
||||||
...metadata,
|
|
||||||
targetColumnMap: generateTargetColumnMap(
|
|
||||||
metadata.type,
|
|
||||||
false,
|
|
||||||
fieldKey,
|
|
||||||
),
|
|
||||||
isNullable,
|
|
||||||
isSystem,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
target.constructor,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const activityTargetMetadata = {
|
|
||||||
nameSingular: 'activityTarget',
|
|
||||||
namePlural: 'activityTargets',
|
|
||||||
labelSingular: 'Activity Target',
|
|
||||||
labelPlural: 'Activity Targets',
|
|
||||||
targetTableName: 'activityTarget',
|
|
||||||
description: 'An activity target',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
// Relations
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activity',
|
|
||||||
label: 'Activity',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget activity',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'person',
|
|
||||||
label: 'Person',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget person',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'activityId',
|
|
||||||
label: 'Activity id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget activity id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: false,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'personId',
|
|
||||||
label: 'Person id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget person id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'companyId',
|
|
||||||
label: 'Company id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'ActivityTarget company id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default activityTargetMetadata;
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const activityMetadata = {
|
|
||||||
nameSingular: 'activity',
|
|
||||||
namePlural: 'activities',
|
|
||||||
labelSingular: 'Activity',
|
|
||||||
labelPlural: 'Activities',
|
|
||||||
targetTableName: 'activity',
|
|
||||||
description: 'An activity',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'title',
|
|
||||||
label: 'Title',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'title',
|
|
||||||
},
|
|
||||||
description: 'Activity title',
|
|
||||||
icon: 'IconNotes',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'body',
|
|
||||||
label: 'Body',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'body',
|
|
||||||
},
|
|
||||||
description: 'Activity body',
|
|
||||||
icon: 'IconList',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'type',
|
|
||||||
label: 'Type',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'type',
|
|
||||||
},
|
|
||||||
description: 'Activity type',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 'Note' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'reminderAt',
|
|
||||||
label: 'Reminder Date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'reminderAt',
|
|
||||||
},
|
|
||||||
description: 'Activity reminder date',
|
|
||||||
icon: 'IconCalendarEvent',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'dueAt',
|
|
||||||
label: 'Due Date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'dueAt',
|
|
||||||
},
|
|
||||||
description: 'Activity due date',
|
|
||||||
icon: 'IconCalendarEvent',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'completedAt',
|
|
||||||
label: 'Completion Date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'completedAt',
|
|
||||||
},
|
|
||||||
description: 'Activity completion date',
|
|
||||||
icon: 'IconCheck',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activityTargets',
|
|
||||||
label: 'Targets',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activity targets',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'attachments',
|
|
||||||
label: 'Attachments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activity attachments',
|
|
||||||
icon: 'IconFileImport',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'comments',
|
|
||||||
label: 'Comments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activity comments',
|
|
||||||
icon: 'IconComment',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'author',
|
|
||||||
label: 'Author',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description:
|
|
||||||
'Activity author. This is the person who created the activity',
|
|
||||||
icon: 'IconUserCircle',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'authorId',
|
|
||||||
label: 'Author id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activity author id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: false,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'assignee',
|
|
||||||
label: 'Assignee',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description:
|
|
||||||
'Acitivity assignee. This is the workspace member assigned to the activity ',
|
|
||||||
icon: 'IconUserCircle',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'assigneeId',
|
|
||||||
label: 'Assignee id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Acitivity assignee id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default activityMetadata;
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const apiKeyMetadata = {
|
|
||||||
nameSingular: 'apiKey',
|
|
||||||
namePlural: 'apiKeys',
|
|
||||||
labelSingular: 'Api Key',
|
|
||||||
labelPlural: 'Api Keys',
|
|
||||||
targetTableName: 'apiKey',
|
|
||||||
description: 'An api key',
|
|
||||||
icon: 'IconRobot',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
description: 'ApiKey name',
|
|
||||||
icon: 'IconLink',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'expiresAt',
|
|
||||||
label: 'Expiration date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'expiresAt',
|
|
||||||
},
|
|
||||||
description: 'ApiKey expiration date',
|
|
||||||
icon: 'IconCalendar',
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'revokedAt',
|
|
||||||
label: 'Revocation date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'revokedAt',
|
|
||||||
},
|
|
||||||
description: 'ApiKey revocation date',
|
|
||||||
icon: 'IconCalendar',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default apiKeyMetadata;
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const attachmentMetadata = {
|
|
||||||
nameSingular: 'attachment',
|
|
||||||
namePlural: 'attachments',
|
|
||||||
labelSingular: 'Attachment',
|
|
||||||
labelPlural: 'Attachments',
|
|
||||||
targetTableName: 'attachment',
|
|
||||||
description: 'An attachment',
|
|
||||||
icon: 'IconFileImport',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
description: 'Attachment name',
|
|
||||||
icon: 'IconFileUpload',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'fullPath',
|
|
||||||
label: 'Full path',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'fullPath',
|
|
||||||
},
|
|
||||||
description: 'Attachment full path',
|
|
||||||
icon: 'IconLink',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'type',
|
|
||||||
label: 'Type',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'type',
|
|
||||||
},
|
|
||||||
description: 'Attachment type',
|
|
||||||
icon: 'IconList',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'author',
|
|
||||||
label: 'Author',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'authorId',
|
|
||||||
},
|
|
||||||
description: 'Attachment author',
|
|
||||||
icon: 'IconCircleUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'authorId',
|
|
||||||
label: 'Author id (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activity author id foreign key',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: false,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activity',
|
|
||||||
label: 'Activity',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'activityId',
|
|
||||||
},
|
|
||||||
description: 'Attachment activity',
|
|
||||||
icon: 'IconNotes',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'person',
|
|
||||||
label: 'Person',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'personId',
|
|
||||||
},
|
|
||||||
description: 'Attachment person',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'companyId',
|
|
||||||
},
|
|
||||||
description: 'Attachment company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default attachmentMetadata;
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const commentMetadata = {
|
|
||||||
nameSingular: 'comment',
|
|
||||||
namePlural: 'comments',
|
|
||||||
labelSingular: 'Comment',
|
|
||||||
labelPlural: 'Comments',
|
|
||||||
targetTableName: 'comment',
|
|
||||||
description: 'A comment',
|
|
||||||
icon: 'IconMessageCircle',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'body',
|
|
||||||
label: 'Body',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'body',
|
|
||||||
},
|
|
||||||
description: 'Comment body',
|
|
||||||
icon: 'IconLink',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'authorId',
|
|
||||||
label: 'Author',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Comment author',
|
|
||||||
icon: 'IconCircleUser',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'author',
|
|
||||||
label: 'Author',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Comment author',
|
|
||||||
icon: 'IconCircleUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activity',
|
|
||||||
label: 'Activity',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Comment activity',
|
|
||||||
icon: 'IconNotes',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'activityId',
|
|
||||||
label: 'Activity',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Comment activity',
|
|
||||||
icon: 'IconNotes',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default commentMetadata;
|
|
||||||
@ -1,212 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const companyMetadata = {
|
|
||||||
nameSingular: 'company',
|
|
||||||
namePlural: 'companies',
|
|
||||||
labelSingular: 'Company',
|
|
||||||
labelPlural: 'Companies',
|
|
||||||
targetTableName: 'company',
|
|
||||||
description: 'A company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: false,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
description: 'The company name',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'domainName',
|
|
||||||
label: 'Domain Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'domainName',
|
|
||||||
},
|
|
||||||
description:
|
|
||||||
'The company website URL. We use this url to fetch the company icon',
|
|
||||||
icon: 'IconLink',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'address',
|
|
||||||
label: 'Address',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'address',
|
|
||||||
},
|
|
||||||
description: 'The company address',
|
|
||||||
icon: 'IconMap',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'employees',
|
|
||||||
label: 'Employees',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'employees',
|
|
||||||
},
|
|
||||||
description: 'Number of employees in the company',
|
|
||||||
icon: 'IconUsers',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.LINK,
|
|
||||||
name: 'linkedinLink',
|
|
||||||
label: 'Linkedin',
|
|
||||||
targetColumnMap: {
|
|
||||||
label: 'linkedinLinkLabel',
|
|
||||||
url: 'linkedinLinkUrl',
|
|
||||||
},
|
|
||||||
description: 'The company Linkedin account',
|
|
||||||
icon: 'IconBrandLinkedin',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.LINK,
|
|
||||||
name: 'xLink',
|
|
||||||
label: 'X',
|
|
||||||
targetColumnMap: {
|
|
||||||
label: 'xLinkLabel',
|
|
||||||
url: 'xLinkUrl',
|
|
||||||
},
|
|
||||||
description: 'The company Twitter/X account',
|
|
||||||
icon: 'IconBrandX',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.CURRENCY,
|
|
||||||
name: 'annualRecurringRevenue',
|
|
||||||
label: 'ARR',
|
|
||||||
targetColumnMap: {
|
|
||||||
amountMicros: 'annualRecurringRevenueAmountMicros',
|
|
||||||
currencyCode: 'annualRecurringRevenueCurrencyCode',
|
|
||||||
},
|
|
||||||
description:
|
|
||||||
'Annual Recurring Revenue: The actual or estimated annual revenue of the company',
|
|
||||||
icon: 'IconMoneybag',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.BOOLEAN,
|
|
||||||
name: 'idealCustomerProfile',
|
|
||||||
label: 'ICP',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'idealCustomerProfile',
|
|
||||||
},
|
|
||||||
description:
|
|
||||||
'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you',
|
|
||||||
icon: 'IconTarget',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'people',
|
|
||||||
label: 'People',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'People linked to the company.',
|
|
||||||
icon: 'IconUsers',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'accountOwner',
|
|
||||||
label: 'Account Owner',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'accountOwnerId',
|
|
||||||
},
|
|
||||||
description:
|
|
||||||
'Your team member responsible for managing the company account',
|
|
||||||
icon: 'IconUserCircle',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'accountOwnerId',
|
|
||||||
label: 'Account Owner ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for account owner',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activityTargets',
|
|
||||||
label: 'Activities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activities tied to the company',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'opportunities',
|
|
||||||
label: 'Opportunities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Opportunities linked to the company.',
|
|
||||||
icon: 'IconTargetArrow',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'favorites',
|
|
||||||
label: 'Favorites',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorites linked to the company',
|
|
||||||
icon: 'IconHeart',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'attachments',
|
|
||||||
label: 'Attachments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Attachments linked to the company.',
|
|
||||||
icon: 'IconFileImport',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default companyMetadata;
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const favoriteMetadata = {
|
|
||||||
nameSingular: 'favorite',
|
|
||||||
namePlural: 'favorites',
|
|
||||||
labelSingular: 'Favorite',
|
|
||||||
labelPlural: 'Favorites',
|
|
||||||
targetTableName: 'favorite',
|
|
||||||
description: 'A favorite',
|
|
||||||
icon: 'IconHeart',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'position',
|
|
||||||
label: 'Position',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'position',
|
|
||||||
},
|
|
||||||
description: 'Favorite position',
|
|
||||||
icon: 'IconList',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 0 },
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'workspaceMember',
|
|
||||||
label: 'Workspace Member',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorite workspace member',
|
|
||||||
icon: 'IconCircleUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'person',
|
|
||||||
label: 'Person',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorite person',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorite company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'workspaceMemberId',
|
|
||||||
label: 'Workspace Member ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for workspace member',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: false,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'personId',
|
|
||||||
label: 'Person ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for person',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'companyId',
|
|
||||||
label: 'Company ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for company',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default favoriteMetadata;
|
|
||||||
@ -1,165 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const opportunityMetadata = {
|
|
||||||
nameSingular: 'opportunity',
|
|
||||||
namePlural: 'opportunities',
|
|
||||||
labelSingular: 'Opportunity',
|
|
||||||
labelPlural: 'Opportunities',
|
|
||||||
targetTableName: 'opportunity',
|
|
||||||
description: 'An opportunity',
|
|
||||||
icon: 'IconTargetArrow',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: false,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.CURRENCY,
|
|
||||||
name: 'amount',
|
|
||||||
label: 'Amount',
|
|
||||||
targetColumnMap: {
|
|
||||||
amountMicros: 'amountAmountMicros',
|
|
||||||
currencyCode: 'amountCurrencyCode',
|
|
||||||
},
|
|
||||||
description: 'Opportunity amount',
|
|
||||||
icon: 'IconCurrencyDollar',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
name: 'closeDate',
|
|
||||||
label: 'Close date',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'closeDate',
|
|
||||||
},
|
|
||||||
description: 'Opportunity close date',
|
|
||||||
icon: 'IconCalendarEvent',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'probability',
|
|
||||||
label: 'Probability',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'probability',
|
|
||||||
},
|
|
||||||
description: 'Opportunity probability',
|
|
||||||
icon: 'IconProgressCheck',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '0' },
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'pipelineStep',
|
|
||||||
label: 'Pipeline Step',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'pipelineStepId',
|
|
||||||
},
|
|
||||||
description: 'Opportunity pipeline step',
|
|
||||||
icon: 'IconKanban',
|
|
||||||
isSystem: true,
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'pointOfContact',
|
|
||||||
label: 'Point of Contact',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'pointOfContactId',
|
|
||||||
},
|
|
||||||
description: 'Opportunity point of contact',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'person',
|
|
||||||
label: 'Person',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'personId',
|
|
||||||
},
|
|
||||||
description: 'Opportunity person',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'companyId',
|
|
||||||
},
|
|
||||||
description: 'Opportunity company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'companyId',
|
|
||||||
label: 'Company ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for company',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'personId',
|
|
||||||
label: 'Person ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for person',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'pointOfContactId',
|
|
||||||
label: 'Point of Contact ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for point of contact',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'pipelineStepId',
|
|
||||||
label: 'Pipeline Step ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for pipeline step',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default opportunityMetadata;
|
|
||||||
@ -1,209 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const personMetadata = {
|
|
||||||
nameSingular: 'person',
|
|
||||||
namePlural: 'people',
|
|
||||||
labelSingular: 'Person',
|
|
||||||
labelPlural: 'People',
|
|
||||||
targetTableName: 'person',
|
|
||||||
description: 'A person',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: false,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.FULL_NAME,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
firstName: 'nameFirstName',
|
|
||||||
lastName: 'nameLastName',
|
|
||||||
},
|
|
||||||
description: 'Contact’s name',
|
|
||||||
icon: 'IconUser',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.EMAIL,
|
|
||||||
name: 'email',
|
|
||||||
label: 'Email',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'email',
|
|
||||||
},
|
|
||||||
description: 'Contact’s Email',
|
|
||||||
icon: 'IconMail',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.LINK,
|
|
||||||
name: 'linkedinLink',
|
|
||||||
label: 'Linkedin',
|
|
||||||
targetColumnMap: {
|
|
||||||
label: 'linkedinLinkLabel',
|
|
||||||
url: 'linkedinLinkUrl',
|
|
||||||
},
|
|
||||||
description: 'Contact’s Linkedin account',
|
|
||||||
icon: 'IconBrandLinkedin',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.LINK,
|
|
||||||
name: 'xLink',
|
|
||||||
label: 'X',
|
|
||||||
targetColumnMap: {
|
|
||||||
label: 'xLinkLabel',
|
|
||||||
url: 'xLinkUrl',
|
|
||||||
},
|
|
||||||
description: 'Contact’s X/Twitter account',
|
|
||||||
icon: 'IconBrandX',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'jobTitle',
|
|
||||||
label: 'Job Title',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'jobTitle',
|
|
||||||
},
|
|
||||||
description: 'Contact’s job title',
|
|
||||||
icon: 'IconBriefcase',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'phone',
|
|
||||||
label: 'Phone',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'phone',
|
|
||||||
},
|
|
||||||
description: 'Contact’s phone number',
|
|
||||||
icon: 'IconPhone',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'city',
|
|
||||||
label: 'City',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'city',
|
|
||||||
},
|
|
||||||
description: 'Contact’s city',
|
|
||||||
icon: 'IconMap',
|
|
||||||
isNullable: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'avatarUrl',
|
|
||||||
label: 'Avatar',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'avatarUrl',
|
|
||||||
},
|
|
||||||
description: 'Contact’s avatar',
|
|
||||||
icon: 'IconFileUpload',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'company',
|
|
||||||
label: 'Company',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Contact’s company',
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'companyId',
|
|
||||||
label: 'Company ID (foreign key)',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Foreign key for company',
|
|
||||||
icon: undefined,
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'pointOfContactForOpportunities',
|
|
||||||
label: 'POC for Opportunities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Point of Contact for Opportunities',
|
|
||||||
icon: 'IconTargetArrow',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'activityTargets',
|
|
||||||
label: 'Activities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activities tied to the contact',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'opportunities',
|
|
||||||
label: 'Opportunities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Opportunities linked to the contact.',
|
|
||||||
icon: 'IconTargetArrow',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'favorites',
|
|
||||||
label: 'Favorites',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorites linked to the contact',
|
|
||||||
icon: 'IconHeart',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'attachments',
|
|
||||||
label: 'Attachments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Attachments linked to the contact.',
|
|
||||||
icon: 'IconFileImport',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default personMetadata;
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const pipelineStepMetadata = {
|
|
||||||
nameSingular: 'pipelineStep',
|
|
||||||
namePlural: 'pipelineSteps',
|
|
||||||
labelSingular: 'Pipeline Step',
|
|
||||||
labelPlural: 'Pipeline Steps',
|
|
||||||
targetTableName: 'pipelineStep',
|
|
||||||
description: 'A pipeline step',
|
|
||||||
icon: 'IconLayoutKanban',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
description: 'Pipeline Step name',
|
|
||||||
icon: 'IconCurrencyDollar',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'color',
|
|
||||||
label: 'Color',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'color',
|
|
||||||
},
|
|
||||||
description: 'Pipeline Step color',
|
|
||||||
icon: 'IconColorSwatch',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'position',
|
|
||||||
label: 'Position',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'position',
|
|
||||||
},
|
|
||||||
description: 'Pipeline Step position',
|
|
||||||
icon: 'IconHierarchy2',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 0 },
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'opportunities',
|
|
||||||
label: 'Opportunities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Opportunities linked to the step.',
|
|
||||||
icon: 'IconTargetArrow',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default pipelineStepMetadata;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const activityRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'activity',
|
|
||||||
toObjectNameSingular: 'activityTarget',
|
|
||||||
fromFieldMetadataName: 'activityTargets',
|
|
||||||
toFieldMetadataName: 'activity',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'activity',
|
|
||||||
toObjectNameSingular: 'attachment',
|
|
||||||
fromFieldMetadataName: 'attachments',
|
|
||||||
toFieldMetadataName: 'activity',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'activity',
|
|
||||||
toObjectNameSingular: 'comment',
|
|
||||||
fromFieldMetadataName: 'comments',
|
|
||||||
toFieldMetadataName: 'activity',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default activityRelationMetadata;
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const companyRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'company',
|
|
||||||
toObjectNameSingular: 'person',
|
|
||||||
fromFieldMetadataName: 'people',
|
|
||||||
toFieldMetadataName: 'company',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'company',
|
|
||||||
toObjectNameSingular: 'favorite',
|
|
||||||
fromFieldMetadataName: 'favorites',
|
|
||||||
toFieldMetadataName: 'company',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'company',
|
|
||||||
toObjectNameSingular: 'attachment',
|
|
||||||
fromFieldMetadataName: 'attachments',
|
|
||||||
toFieldMetadataName: 'company',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'company',
|
|
||||||
toObjectNameSingular: 'opportunity',
|
|
||||||
fromFieldMetadataName: 'opportunities',
|
|
||||||
toFieldMetadataName: 'company',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'company',
|
|
||||||
toObjectNameSingular: 'activityTarget',
|
|
||||||
fromFieldMetadataName: 'activityTargets',
|
|
||||||
toFieldMetadataName: 'company',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default companyRelationMetadata;
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const personRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'person',
|
|
||||||
toObjectNameSingular: 'favorite',
|
|
||||||
fromFieldMetadataName: 'favorites',
|
|
||||||
toFieldMetadataName: 'person',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'person',
|
|
||||||
toObjectNameSingular: 'attachment',
|
|
||||||
fromFieldMetadataName: 'attachments',
|
|
||||||
toFieldMetadataName: 'person',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'person',
|
|
||||||
toObjectNameSingular: 'opportunity',
|
|
||||||
fromFieldMetadataName: 'opportunities',
|
|
||||||
toFieldMetadataName: 'person',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'person',
|
|
||||||
toObjectNameSingular: 'opportunity',
|
|
||||||
fromFieldMetadataName: 'pointOfContactForOpportunities',
|
|
||||||
toFieldMetadataName: 'pointOfContact',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'person',
|
|
||||||
toObjectNameSingular: 'activityTarget',
|
|
||||||
fromFieldMetadataName: 'activityTargets',
|
|
||||||
toFieldMetadataName: 'person',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default personRelationMetadata;
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const pipelineStepRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'pipelineStep',
|
|
||||||
toObjectNameSingular: 'opportunity',
|
|
||||||
fromFieldMetadataName: 'opportunities',
|
|
||||||
toFieldMetadataName: 'pipelineStep',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default pipelineStepRelationMetadata;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const viewRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'view',
|
|
||||||
toObjectNameSingular: 'viewField',
|
|
||||||
fromFieldMetadataName: 'viewFields',
|
|
||||||
toFieldMetadataName: 'view',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'view',
|
|
||||||
toObjectNameSingular: 'viewFilter',
|
|
||||||
fromFieldMetadataName: 'viewFilters',
|
|
||||||
toFieldMetadataName: 'view',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'view',
|
|
||||||
toObjectNameSingular: 'viewSort',
|
|
||||||
fromFieldMetadataName: 'viewSorts',
|
|
||||||
toFieldMetadataName: 'view',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default viewRelationMetadata;
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
|
||||||
|
|
||||||
const workspaceMemberRelationMetadata = [
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'company',
|
|
||||||
fromFieldMetadataName: 'accountOwnerForCompanies',
|
|
||||||
toFieldMetadataName: 'accountOwner',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'favorite',
|
|
||||||
fromFieldMetadataName: 'favorites',
|
|
||||||
toFieldMetadataName: 'workspaceMember',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'activity',
|
|
||||||
fromFieldMetadataName: 'authoredActivities',
|
|
||||||
toFieldMetadataName: 'author',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'activity',
|
|
||||||
fromFieldMetadataName: 'assignedActivities',
|
|
||||||
toFieldMetadataName: 'assignee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'comment',
|
|
||||||
fromFieldMetadataName: 'authoredComments',
|
|
||||||
toFieldMetadataName: 'author',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
|
||||||
fromObjectNameSingular: 'workspaceMember',
|
|
||||||
toObjectNameSingular: 'attachment',
|
|
||||||
fromFieldMetadataName: 'authoredAttachments',
|
|
||||||
toFieldMetadataName: 'author',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default workspaceMemberRelationMetadata;
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
import apiKeyMetadata from 'src/workspace/workspace-manager/standard-objects/api-key';
|
|
||||||
import {
|
|
||||||
FieldMetadataEntity,
|
|
||||||
FieldMetadataType,
|
|
||||||
} from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import activityMetadata from 'src/workspace/workspace-manager/standard-objects/activity';
|
|
||||||
import activityTargetMetadata from 'src/workspace/workspace-manager/standard-objects/activity-target';
|
|
||||||
import attachmentMetadata from 'src/workspace/workspace-manager/standard-objects/attachment';
|
|
||||||
import commentMetadata from 'src/workspace/workspace-manager/standard-objects/comment';
|
|
||||||
import companyMetadata from 'src/workspace/workspace-manager/standard-objects/company';
|
|
||||||
import favoriteMetadata from 'src/workspace/workspace-manager/standard-objects/favorite';
|
|
||||||
import opportunityMetadata from 'src/workspace/workspace-manager/standard-objects/opportunity';
|
|
||||||
import personMetadata from 'src/workspace/workspace-manager/standard-objects/person';
|
|
||||||
import pipelineStepMetadata from 'src/workspace/workspace-manager/standard-objects/pipeline-step';
|
|
||||||
import viewMetadata from 'src/workspace/workspace-manager/standard-objects/view';
|
|
||||||
import viewFieldMetadata from 'src/workspace/workspace-manager/standard-objects/view-field';
|
|
||||||
import viewFilterMetadata from 'src/workspace/workspace-manager/standard-objects/view-filter';
|
|
||||||
import viewSortMetadata from 'src/workspace/workspace-manager/standard-objects/view-sort';
|
|
||||||
import workspaceMemberMetadata from 'src/workspace/workspace-manager/standard-objects/workspace-member';
|
|
||||||
import connectedAccountMetadata from 'src/workspace/workspace-manager/standard-objects/connected-account';
|
|
||||||
|
|
||||||
export const standardObjectsMetadata = {
|
|
||||||
activityTarget: activityTargetMetadata,
|
|
||||||
activity: activityMetadata,
|
|
||||||
apiKey: apiKeyMetadata,
|
|
||||||
attachment: attachmentMetadata,
|
|
||||||
comment: commentMetadata,
|
|
||||||
company: companyMetadata,
|
|
||||||
connectedAccount: connectedAccountMetadata,
|
|
||||||
favorite: favoriteMetadata,
|
|
||||||
opportunity: opportunityMetadata,
|
|
||||||
person: personMetadata,
|
|
||||||
pipelineStep: pipelineStepMetadata,
|
|
||||||
viewField: viewFieldMetadata,
|
|
||||||
viewFilter: viewFilterMetadata,
|
|
||||||
viewSort: viewSortMetadata,
|
|
||||||
view: viewMetadata,
|
|
||||||
workspaceMember: workspaceMemberMetadata,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const basicFieldsMetadata: Partial<FieldMetadataEntity>[] = [
|
|
||||||
{
|
|
||||||
name: 'id',
|
|
||||||
label: 'Id',
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'id',
|
|
||||||
},
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: true,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
defaultValue: { type: 'uuid' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'createdAt',
|
|
||||||
label: 'Creation date',
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'createdAt',
|
|
||||||
},
|
|
||||||
icon: 'IconCalendar',
|
|
||||||
isNullable: true,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
defaultValue: { type: 'now' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatedAt',
|
|
||||||
label: 'Update date',
|
|
||||||
type: FieldMetadataType.DATE_TIME,
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'updatedAt',
|
|
||||||
},
|
|
||||||
icon: 'IconCalendar',
|
|
||||||
isNullable: true,
|
|
||||||
isCustom: false,
|
|
||||||
isSystem: true,
|
|
||||||
isActive: true,
|
|
||||||
defaultValue: { type: 'now' },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import activityRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/activity';
|
|
||||||
import companyRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/company';
|
|
||||||
import personRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/person';
|
|
||||||
import pipelineStepRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/pipeline-step';
|
|
||||||
import viewRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/view';
|
|
||||||
import workspaceMemberRelationMetadata from 'src/workspace/workspace-manager/standard-objects/relations/workspace-member';
|
|
||||||
|
|
||||||
export const standardObjectRelationMetadata = [
|
|
||||||
...activityRelationMetadata,
|
|
||||||
...companyRelationMetadata,
|
|
||||||
...personRelationMetadata,
|
|
||||||
...pipelineStepRelationMetadata,
|
|
||||||
...viewRelationMetadata,
|
|
||||||
...workspaceMemberRelationMetadata,
|
|
||||||
];
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const viewFieldMetadata = {
|
|
||||||
nameSingular: 'viewField',
|
|
||||||
namePlural: 'viewFields',
|
|
||||||
labelSingular: 'View Field',
|
|
||||||
labelPlural: 'View Fields',
|
|
||||||
targetTableName: 'viewField',
|
|
||||||
description: '(System) View Fields',
|
|
||||||
icon: 'IconTag',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'fieldMetadataId',
|
|
||||||
label: 'Field Metadata Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'fieldMetadataId',
|
|
||||||
},
|
|
||||||
description: 'View Field target field',
|
|
||||||
icon: 'IconTag',
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.BOOLEAN,
|
|
||||||
name: 'isVisible',
|
|
||||||
label: 'Visible',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'isVisible',
|
|
||||||
},
|
|
||||||
description: 'View Field visibility',
|
|
||||||
icon: 'IconEye',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'size',
|
|
||||||
label: 'Size',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'size',
|
|
||||||
},
|
|
||||||
description: 'View Field size',
|
|
||||||
icon: 'IconEye',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'position',
|
|
||||||
label: 'Position',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'position',
|
|
||||||
},
|
|
||||||
description: 'View Field position',
|
|
||||||
icon: 'IconList',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'view',
|
|
||||||
label: 'View',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Field related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'viewId',
|
|
||||||
label: 'View Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'viewId',
|
|
||||||
},
|
|
||||||
description: 'View field related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default viewFieldMetadata;
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const viewFilterMetadata = {
|
|
||||||
nameSingular: 'viewFilter',
|
|
||||||
namePlural: 'viewFilters',
|
|
||||||
labelSingular: 'View Filter',
|
|
||||||
labelPlural: 'View Filters',
|
|
||||||
targetTableName: 'viewFilter',
|
|
||||||
description: '(System) View Filters',
|
|
||||||
icon: 'IconFilterBolt',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'fieldMetadataId',
|
|
||||||
label: 'Field Metadata Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'fieldMetadataId',
|
|
||||||
},
|
|
||||||
description: 'View Filter target field',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'operand',
|
|
||||||
label: 'Operand',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'operand',
|
|
||||||
},
|
|
||||||
description: 'View Filter operand',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 'Contains' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'value',
|
|
||||||
label: 'Value',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'value',
|
|
||||||
},
|
|
||||||
description: 'View Filter value',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'displayValue',
|
|
||||||
label: 'Display Value',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'displayValue',
|
|
||||||
},
|
|
||||||
description: 'View Filter Display Value',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'view',
|
|
||||||
label: 'View',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Filter related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'viewId',
|
|
||||||
label: 'View Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'viewId',
|
|
||||||
},
|
|
||||||
description: 'View Filter related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default viewFilterMetadata;
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const viewSortMetadata = {
|
|
||||||
nameSingular: 'viewSort',
|
|
||||||
namePlural: 'viewSorts',
|
|
||||||
labelSingular: 'View Sort',
|
|
||||||
labelPlural: 'View Sorts',
|
|
||||||
targetTableName: 'viewSort',
|
|
||||||
description: '(System) View Sorts',
|
|
||||||
icon: 'IconArrowsSort',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'fieldMetadataId',
|
|
||||||
label: 'Field Metadata Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'fieldMetadataId',
|
|
||||||
},
|
|
||||||
description: 'View Sort target field',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'direction',
|
|
||||||
label: 'Direction',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'direction',
|
|
||||||
},
|
|
||||||
description: 'View Sort direction',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 'asc' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'view',
|
|
||||||
label: 'View',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Sort related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'viewId',
|
|
||||||
label: 'View Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'viewId',
|
|
||||||
},
|
|
||||||
description: 'View Sort related view',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default viewSortMetadata;
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const viewMetadata = {
|
|
||||||
nameSingular: 'view',
|
|
||||||
namePlural: 'views',
|
|
||||||
labelSingular: 'View',
|
|
||||||
labelPlural: 'Views',
|
|
||||||
targetTableName: 'view',
|
|
||||||
description: '(System) Views',
|
|
||||||
icon: 'IconLayoutCollage',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
description: 'View name',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'objectMetadataId',
|
|
||||||
label: 'Object Metadata Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'objectMetadataId',
|
|
||||||
},
|
|
||||||
description: 'View target object',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'type',
|
|
||||||
label: 'Type',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'type',
|
|
||||||
},
|
|
||||||
description: 'View type',
|
|
||||||
icon: null,
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'viewFields',
|
|
||||||
label: 'View Fields',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Fields',
|
|
||||||
icon: 'IconTag',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'viewSorts',
|
|
||||||
label: 'View Sorts',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Sorts',
|
|
||||||
icon: 'IconArrowsSort',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'viewFilters',
|
|
||||||
label: 'View Filters',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'View Filters',
|
|
||||||
icon: 'IconFilterBolt',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default viewMetadata;
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const webhookMetadata = {
|
|
||||||
nameSingular: 'webhook',
|
|
||||||
namePlural: 'webhooks',
|
|
||||||
labelSingular: 'Webhook',
|
|
||||||
labelPlural: 'Webhooks',
|
|
||||||
targetTableName: 'webhook',
|
|
||||||
description: 'A webhook',
|
|
||||||
icon: 'IconRobot',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'targetUrl',
|
|
||||||
label: 'Target Url',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'targetUrl',
|
|
||||||
},
|
|
||||||
description: 'Webhook target url',
|
|
||||||
icon: 'IconLink',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'operation',
|
|
||||||
label: 'Operation',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'operation',
|
|
||||||
},
|
|
||||||
description: 'Webhook operation',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default webhookMetadata;
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
|
|
||||||
const workspaceMemberMetadata = {
|
|
||||||
nameSingular: 'workspaceMember',
|
|
||||||
namePlural: 'workspaceMembers',
|
|
||||||
labelSingular: 'Workspace Member',
|
|
||||||
labelPlural: 'Workspace Members',
|
|
||||||
targetTableName: 'workspaceMember',
|
|
||||||
description: 'A workspace member',
|
|
||||||
icon: 'IconUserCircle',
|
|
||||||
isActive: true,
|
|
||||||
isSystem: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.FULL_NAME,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
targetColumnMap: {
|
|
||||||
firstName: 'nameFirstName',
|
|
||||||
lastName: 'nameLastName',
|
|
||||||
},
|
|
||||||
description: 'Workspace member name',
|
|
||||||
icon: 'IconCircleUser',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { firstName: '', lastName: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'colorScheme',
|
|
||||||
label: 'Color Scheme',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'colorScheme',
|
|
||||||
},
|
|
||||||
description: 'Preferred color scheme',
|
|
||||||
icon: 'IconColorSwatch',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 'Light' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'locale',
|
|
||||||
label: 'Language',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'locale',
|
|
||||||
},
|
|
||||||
description: 'Preferred language',
|
|
||||||
icon: 'IconLanguage',
|
|
||||||
isNullable: false,
|
|
||||||
defaultValue: { value: 'en' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'avatarUrl',
|
|
||||||
label: 'Avatar Url',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'avatarUrl',
|
|
||||||
},
|
|
||||||
description: 'Workspace member avatar',
|
|
||||||
icon: 'IconFileUpload',
|
|
||||||
isNullable: true,
|
|
||||||
isSystem: false,
|
|
||||||
defaultValue: { value: '' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.UUID,
|
|
||||||
name: 'userId',
|
|
||||||
label: 'User Id',
|
|
||||||
targetColumnMap: {
|
|
||||||
value: 'userId',
|
|
||||||
},
|
|
||||||
description: 'Associated User Id',
|
|
||||||
icon: 'IconCircleUsers',
|
|
||||||
isNullable: false,
|
|
||||||
isSystem: false,
|
|
||||||
},
|
|
||||||
// Relations
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'authoredActivities',
|
|
||||||
label: 'Authored activities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activities created by the workspace member',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'assignedActivities',
|
|
||||||
label: 'Assigned activities',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Activities assigned to the workspace member',
|
|
||||||
icon: 'IconCheckbox',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'favorites',
|
|
||||||
label: 'Favorites',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Favorites linked to the workspace member',
|
|
||||||
icon: 'IconHeart',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'accountOwnerForCompanies',
|
|
||||||
label: 'Account Owner For Companies',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Account owner for companies',
|
|
||||||
icon: 'IconBriefcase',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'authoredAttachments',
|
|
||||||
label: 'Authored attachments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Attachments created by the workspace member',
|
|
||||||
icon: 'IconFileImport',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'authoredComments',
|
|
||||||
label: 'Authored comments',
|
|
||||||
targetColumnMap: {},
|
|
||||||
description: 'Authored comments',
|
|
||||||
icon: 'IconComment',
|
|
||||||
isNullable: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default workspaceMemberMetadata;
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/base.object-metadata';
|
|
||||||
|
|
||||||
export class MetadataParser {
|
|
||||||
static parseMetadata(
|
|
||||||
metadata: typeof BaseObjectMetadata,
|
|
||||||
workspaceId: string,
|
|
||||||
dataSourceId: string,
|
|
||||||
) {
|
|
||||||
const objectMetadata = Reflect.getMetadata('objectMetadata', metadata);
|
|
||||||
const fieldMetadata = Reflect.getMetadata('fieldMetadata', metadata);
|
|
||||||
|
|
||||||
if (objectMetadata) {
|
|
||||||
const fields = Object.values(fieldMetadata);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...objectMetadata,
|
|
||||||
workspaceId,
|
|
||||||
dataSourceId,
|
|
||||||
fields: fields.map((field: FieldMetadataEntity) => ({
|
|
||||||
...field,
|
|
||||||
workspaceId,
|
|
||||||
isSystem: objectMetadata.isSystem || field.isSystem,
|
|
||||||
defaultValue: field.defaultValue || null, // TODO: use default default value based on field type
|
|
||||||
options: field.options || null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
static parseAllMetadata(
|
|
||||||
metadata: (typeof BaseObjectMetadata)[],
|
|
||||||
workspaceId: string,
|
|
||||||
dataSourceId: string,
|
|
||||||
) {
|
|
||||||
return metadata.map((_metadata) =>
|
|
||||||
MetadataParser.parseMetadata(_metadata, workspaceId, dataSourceId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||||
import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module';
|
import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module';
|
||||||
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||||
import { WorkspaceMigrationRunnerModule } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.module';
|
|
||||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||||
import { RelationMetadataModule } from 'src/metadata/relation-metadata/relation-metadata.module';
|
import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module';
|
||||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
|
||||||
|
|
||||||
import { WorkspaceManagerService } from './workspace-manager.service';
|
import { WorkspaceManagerService } from './workspace-manager.service';
|
||||||
|
|
||||||
@ -16,14 +12,9 @@ import { WorkspaceManagerService } from './workspace-manager.service';
|
|||||||
imports: [
|
imports: [
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
WorkspaceMigrationModule,
|
WorkspaceMigrationModule,
|
||||||
WorkspaceMigrationRunnerModule,
|
|
||||||
ObjectMetadataModule,
|
ObjectMetadataModule,
|
||||||
DataSourceModule,
|
DataSourceModule,
|
||||||
RelationMetadataModule,
|
WorkspaceSyncMetadataModule,
|
||||||
TypeOrmModule.forFeature(
|
|
||||||
[FieldMetadataEntity, ObjectMetadataEntity],
|
|
||||||
'metadata',
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
exports: [WorkspaceManagerService],
|
exports: [WorkspaceManagerService],
|
||||||
providers: [WorkspaceManagerService],
|
providers: [WorkspaceManagerService],
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import diff from 'microdiff';
|
|
||||||
|
|
||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
@ -12,41 +8,16 @@ import { standardObjectsPrefillData } from 'src/workspace/workspace-manager/stan
|
|||||||
import { demoObjectsPrefillData } from 'src/workspace/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data';
|
import { demoObjectsPrefillData } from 'src/workspace/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data';
|
||||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||||
import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity';
|
import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity';
|
||||||
import { RelationMetadataService } from 'src/metadata/relation-metadata/relation-metadata.service';
|
import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync.metadata.service';
|
||||||
import { standardObjectRelationMetadata } from 'src/workspace/workspace-manager/standard-objects/standard-object-relation-metadata';
|
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
|
||||||
import {
|
|
||||||
FieldMetadataEntity,
|
|
||||||
FieldMetadataType,
|
|
||||||
} from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import { MetadataParser } from 'src/workspace/workspace-manager/utils/metadata.parser';
|
|
||||||
import { WebhookObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/webook.object-metadata';
|
|
||||||
import { ApiKeyObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/api-key.object-metadata';
|
|
||||||
import { ViewSortObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/view-sort.object-metadata';
|
|
||||||
import {
|
|
||||||
filterIgnoredProperties,
|
|
||||||
mapObjectMetadataByUniqueIdentifier,
|
|
||||||
} from 'src/workspace/workspace-manager/utils/sync-metadata.util';
|
|
||||||
|
|
||||||
import {
|
|
||||||
basicFieldsMetadata,
|
|
||||||
standardObjectsMetadata,
|
|
||||||
} from './standard-objects/standard-object-metadata';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceManagerService {
|
export class WorkspaceManagerService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
|
||||||
private readonly objectMetadataService: ObjectMetadataService,
|
private readonly objectMetadataService: ObjectMetadataService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
private readonly relationMetadataService: RelationMetadataService,
|
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
|
||||||
|
|
||||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
|
||||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
|
||||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
|
||||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,22 +39,14 @@ export class WorkspaceManagerService {
|
|||||||
|
|
||||||
await this.setWorkspaceMaxRow(workspaceId, schemaName);
|
await this.setWorkspaceMaxRow(workspaceId, schemaName);
|
||||||
|
|
||||||
await this.workspaceMigrationService.insertStandardMigrations(workspaceId);
|
await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata(
|
||||||
|
dataSourceMetadata.id,
|
||||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const createdObjectMetadata =
|
|
||||||
await this.createStandardObjectsAndFieldsMetadata(
|
|
||||||
dataSourceMetadata.id,
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.prefillWorkspaceWithStandardObjects(
|
await this.prefillWorkspaceWithStandardObjects(
|
||||||
dataSourceMetadata,
|
dataSourceMetadata,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
createdObjectMetadata,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,321 +69,12 @@ export class WorkspaceManagerService {
|
|||||||
|
|
||||||
await this.setWorkspaceMaxRow(workspaceId, schemaName);
|
await this.setWorkspaceMaxRow(workspaceId, schemaName);
|
||||||
|
|
||||||
await this.workspaceMigrationService.insertStandardMigrations(workspaceId);
|
await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata(
|
||||||
|
dataSourceMetadata.id,
|
||||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const createdObjectMetadata =
|
await this.prefillWorkspaceWithDemoObjects(dataSourceMetadata, workspaceId);
|
||||||
await this.createStandardObjectsAndFieldsMetadata(
|
|
||||||
dataSourceMetadata.id,
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.prefillWorkspaceWithDemoObjects(
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceId,
|
|
||||||
createdObjectMetadata,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Create all standard objects and fields metadata for a given workspace
|
|
||||||
*
|
|
||||||
* @param dataSourceId
|
|
||||||
* @param workspaceId
|
|
||||||
*/
|
|
||||||
public async createStandardObjectsAndFieldsMetadata(
|
|
||||||
dataSourceId: string,
|
|
||||||
workspaceId: string,
|
|
||||||
): Promise<ObjectMetadataEntity[]> {
|
|
||||||
const createdObjectMetadata = await this.objectMetadataService.createMany(
|
|
||||||
Object.values(standardObjectsMetadata).map((objectMetadata: any) => ({
|
|
||||||
...objectMetadata,
|
|
||||||
dataSourceId,
|
|
||||||
workspaceId,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
fields: [...basicFieldsMetadata, ...objectMetadata.fields].map(
|
|
||||||
(field) => ({
|
|
||||||
...field,
|
|
||||||
workspaceId,
|
|
||||||
isCustom: false,
|
|
||||||
isActive: true,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.relationMetadataService.createMany(
|
|
||||||
Object.values(standardObjectRelationMetadata).map((relationMetadata) =>
|
|
||||||
this.createStandardObjectRelations(
|
|
||||||
workspaceId,
|
|
||||||
createdObjectMetadata,
|
|
||||||
relationMetadata,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return createdObjectMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param workspaceId
|
|
||||||
* @param createdObjectMetadata
|
|
||||||
* @param relationMetadata
|
|
||||||
* @returns Partial<RelationMetadataEntity>
|
|
||||||
*/
|
|
||||||
private createStandardObjectRelations(
|
|
||||||
workspaceId: string,
|
|
||||||
createdObjectMetadata: ObjectMetadataEntity[],
|
|
||||||
relationMetadata: any,
|
|
||||||
) {
|
|
||||||
const createdObjectMetadataByNameSingular = createdObjectMetadata.reduce(
|
|
||||||
(acc, curr) => {
|
|
||||||
acc[curr.nameSingular] = curr;
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const fromObjectMetadata =
|
|
||||||
createdObjectMetadataByNameSingular[
|
|
||||||
relationMetadata.fromObjectNameSingular
|
|
||||||
];
|
|
||||||
const toObjectMetadata =
|
|
||||||
createdObjectMetadataByNameSingular[
|
|
||||||
relationMetadata.toObjectNameSingular
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!fromObjectMetadata) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not find created object metadata with
|
|
||||||
fromObjectNameSingular: ${relationMetadata.fromObjectNameSingular}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!toObjectMetadata) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not find created object metadata with
|
|
||||||
toObjectNameSingular: ${relationMetadata.toObjectNameSingular}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromFieldMetadata = createdObjectMetadataByNameSingular[
|
|
||||||
relationMetadata.fromObjectNameSingular
|
|
||||||
]?.fields.find(
|
|
||||||
(field: FieldMetadataEntity) =>
|
|
||||||
field.type === FieldMetadataType.RELATION &&
|
|
||||||
field.name === relationMetadata.fromFieldMetadataName,
|
|
||||||
);
|
|
||||||
|
|
||||||
const toFieldMetadata = createdObjectMetadataByNameSingular[
|
|
||||||
relationMetadata.toObjectNameSingular
|
|
||||||
]?.fields.find(
|
|
||||||
(field: FieldMetadataEntity) =>
|
|
||||||
field.type === FieldMetadataType.RELATION &&
|
|
||||||
field.name === relationMetadata.toFieldMetadataName,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!fromFieldMetadata) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not find created field metadata with
|
|
||||||
fromFieldMetadataName: ${relationMetadata.fromFieldMetadataName}
|
|
||||||
for object: ${relationMetadata.fromObjectNameSingular}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!toFieldMetadata) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not find created field metadata with
|
|
||||||
toFieldMetadataName: ${relationMetadata.toFieldMetadataName}
|
|
||||||
for object: ${relationMetadata.toObjectNameSingular}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
fromObjectMetadataId: fromObjectMetadata.id,
|
|
||||||
toObjectMetadataId: toObjectMetadata.id,
|
|
||||||
workspaceId,
|
|
||||||
relationType: relationMetadata.type,
|
|
||||||
fromFieldMetadataId: fromFieldMetadata.id,
|
|
||||||
toFieldMetadataId: toFieldMetadata.id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Sync all standard objects and fields metadata for a given workspace and data source
|
|
||||||
* This will update the metadata if it has changed and generate migrations based on the diff.
|
|
||||||
*
|
|
||||||
* @param dataSourceId
|
|
||||||
* @param workspaceId
|
|
||||||
*/
|
|
||||||
public async syncStandardObjectsAndFieldsMetadata(
|
|
||||||
dataSourceId: string,
|
|
||||||
workspaceId: string,
|
|
||||||
) {
|
|
||||||
const standardObjects = MetadataParser.parseAllMetadata(
|
|
||||||
[WebhookObjectMetadata, ApiKeyObjectMetadata, ViewSortObjectMetadata],
|
|
||||||
workspaceId,
|
|
||||||
dataSourceId,
|
|
||||||
);
|
|
||||||
const objectsInDB = await this.objectMetadataRepository.find({
|
|
||||||
where: { workspaceId, dataSourceId, isCustom: false },
|
|
||||||
relations: ['fields'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const objectsInDBByName = mapObjectMetadataByUniqueIdentifier(objectsInDB);
|
|
||||||
const standardObjectsByName =
|
|
||||||
mapObjectMetadataByUniqueIdentifier(standardObjects);
|
|
||||||
|
|
||||||
const objectsToCreate: ObjectMetadataEntity[] = [];
|
|
||||||
const objectsToDelete = objectsInDB.filter(
|
|
||||||
(objectInDB) => !standardObjectsByName[objectInDB.nameSingular],
|
|
||||||
);
|
|
||||||
const objectsToUpdate: Record<string, ObjectMetadataEntity> = {};
|
|
||||||
|
|
||||||
const fieldsToCreate: FieldMetadataEntity[] = [];
|
|
||||||
const fieldsToDelete: FieldMetadataEntity[] = [];
|
|
||||||
const fieldsToUpdate: Record<string, FieldMetadataEntity> = {};
|
|
||||||
|
|
||||||
for (const standardObjectName in standardObjectsByName) {
|
|
||||||
const standardObject = standardObjectsByName[standardObjectName];
|
|
||||||
const objectInDB = objectsInDBByName[standardObjectName];
|
|
||||||
|
|
||||||
if (!objectInDB) {
|
|
||||||
objectsToCreate.push(standardObject);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deconstruct fields and compare objects and fields independently
|
|
||||||
const { fields: objectInDBFields, ...objectInDBWithoutFields } =
|
|
||||||
objectInDB;
|
|
||||||
const { fields: standardObjectFields, ...standardObjectWithoutFields } =
|
|
||||||
standardObject;
|
|
||||||
|
|
||||||
const objectPropertiesToIgnore = [
|
|
||||||
'id',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
|
||||||
'labelIdentifierFieldMetadataId',
|
|
||||||
'imageIdentifierFieldMetadataId',
|
|
||||||
];
|
|
||||||
const objectDiffWithoutIgnoredProperties = filterIgnoredProperties(
|
|
||||||
objectInDBWithoutFields,
|
|
||||||
objectPropertiesToIgnore,
|
|
||||||
);
|
|
||||||
|
|
||||||
const fieldPropertiesToIgnore = [
|
|
||||||
'id',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
|
||||||
'objectMetadataId',
|
|
||||||
];
|
|
||||||
const objectInDBFieldsWithoutDefaultFields = Object.fromEntries(
|
|
||||||
Object.entries(objectInDBFields).map(([key, value]) => {
|
|
||||||
if (value === null || typeof value !== 'object') {
|
|
||||||
return [key, value];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [key, filterIgnoredProperties(value, fieldPropertiesToIgnore)];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compare objects
|
|
||||||
const objectDiff = diff(
|
|
||||||
objectDiffWithoutIgnoredProperties,
|
|
||||||
standardObjectWithoutFields,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compare fields
|
|
||||||
const fieldsDiff = diff(
|
|
||||||
objectInDBFieldsWithoutDefaultFields,
|
|
||||||
standardObjectFields,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const diff of objectDiff) {
|
|
||||||
// We only handle CHANGE here as REMOVE and CREATE are handled earlier.
|
|
||||||
if (diff.type === 'CHANGE') {
|
|
||||||
const property = diff.path[0];
|
|
||||||
|
|
||||||
objectsToUpdate[objectInDB.id] = {
|
|
||||||
...objectsToUpdate[objectInDB.id],
|
|
||||||
[property]: diff.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const diff of fieldsDiff) {
|
|
||||||
if (diff.type === 'CREATE') {
|
|
||||||
const fieldName = diff.path[0];
|
|
||||||
const fieldMetadata = standardObjectFields[fieldName];
|
|
||||||
|
|
||||||
fieldsToCreate.push(fieldMetadata);
|
|
||||||
}
|
|
||||||
if (diff.type === 'CHANGE') {
|
|
||||||
const fieldName = diff.path[0];
|
|
||||||
const property = diff.path[diff.path.length - 1];
|
|
||||||
const fieldMetadata = objectInDBFields[fieldName];
|
|
||||||
|
|
||||||
fieldsToUpdate[fieldMetadata.id] = {
|
|
||||||
...fieldsToUpdate[fieldMetadata.id],
|
|
||||||
[property]: diff.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (diff.type === 'REMOVE') {
|
|
||||||
const fieldName = diff.path[0];
|
|
||||||
const fieldMetadata = objectInDBFields[fieldName];
|
|
||||||
|
|
||||||
fieldsToDelete.push(fieldMetadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// console.log(standardObjectName + ':objectDiff', objectDiff);
|
|
||||||
// console.log(standardObjectName + ':fieldsDiff', fieldsDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Sync relationMetadata
|
|
||||||
// NOTE: Relations are handled like any field during the diff, so we ignore the relationMetadata table
|
|
||||||
// during the diff as it depends on the 2 fieldMetadata that we will compare here.
|
|
||||||
// However we need to make sure the relationMetadata table is in sync with the fieldMetadata table.
|
|
||||||
|
|
||||||
// TODO: Use transactions
|
|
||||||
// CREATE OBJECTS
|
|
||||||
try {
|
|
||||||
await this.objectMetadataRepository.save(objectsToCreate);
|
|
||||||
// UPDATE OBJECTS, this is not optimal as we are running n queries here.
|
|
||||||
for (const [key, value] of Object.entries(objectsToUpdate)) {
|
|
||||||
await this.objectMetadataRepository.update(key, value);
|
|
||||||
}
|
|
||||||
// DELETE OBJECTS
|
|
||||||
if (objectsToDelete.length > 0) {
|
|
||||||
await this.objectMetadataRepository.delete(
|
|
||||||
objectsToDelete.map((object) => object.id),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CREATE FIELDS
|
|
||||||
await this.fieldMetadataRepository.save(fieldsToCreate);
|
|
||||||
// UPDATE FIELDS
|
|
||||||
for (const [key, value] of Object.entries(fieldsToUpdate)) {
|
|
||||||
await this.fieldMetadataRepository.update(key, value);
|
|
||||||
}
|
|
||||||
// DELETE FIELDS
|
|
||||||
if (fieldsToDelete.length > 0) {
|
|
||||||
await this.fieldMetadataRepository.delete(
|
|
||||||
fieldsToDelete.map((field) => field.id),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Sync of standard objects failed with:', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Create migrations based on diff from above.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -451,7 +105,6 @@ export class WorkspaceManagerService {
|
|||||||
private async prefillWorkspaceWithStandardObjects(
|
private async prefillWorkspaceWithStandardObjects(
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
createdObjectMetadata: ObjectMetadataEntity[],
|
|
||||||
) {
|
) {
|
||||||
const workspaceDataSource =
|
const workspaceDataSource =
|
||||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||||
@ -461,6 +114,10 @@ export class WorkspaceManagerService {
|
|||||||
if (!workspaceDataSource) {
|
if (!workspaceDataSource) {
|
||||||
throw new Error('Could not connect to workspace data source');
|
throw new Error('Could not connect to workspace data source');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createdObjectMetadata =
|
||||||
|
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||||
|
|
||||||
await standardObjectsPrefillData(
|
await standardObjectsPrefillData(
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
dataSourceMetadata.schema,
|
dataSourceMetadata.schema,
|
||||||
@ -478,7 +135,6 @@ export class WorkspaceManagerService {
|
|||||||
private async prefillWorkspaceWithDemoObjects(
|
private async prefillWorkspaceWithDemoObjects(
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
createdObjectMetadata: ObjectMetadataEntity[],
|
|
||||||
) {
|
) {
|
||||||
const workspaceDataSource =
|
const workspaceDataSource =
|
||||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||||
@ -489,6 +145,9 @@ export class WorkspaceManagerService {
|
|||||||
throw new Error('Could not connect to workspace data source');
|
throw new Error('Could not connect to workspace data source');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createdObjectMetadata =
|
||||||
|
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||||
|
|
||||||
await demoObjectsPrefillData(
|
await demoObjectsPrefillData(
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
dataSourceMetadata.schema,
|
dataSourceMetadata.schema,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||||
|
|
||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync.metadata.service';
|
||||||
|
|
||||||
// TODO: implement dry-run
|
// TODO: implement dry-run
|
||||||
interface RunWorkspaceMigrationsOptions {
|
interface RunWorkspaceMigrationsOptions {
|
||||||
@ -14,7 +14,7 @@ interface RunWorkspaceMigrationsOptions {
|
|||||||
})
|
})
|
||||||
export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workspaceManagerService: WorkspaceManagerService,
|
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@ -29,9 +29,7 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
|||||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
options.workspaceId,
|
options.workspaceId,
|
||||||
);
|
);
|
||||||
|
await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata(
|
||||||
// TODO: This solution could be improved, using a diff for example, we should not have to delete all metadata and recreate them.
|
|
||||||
await this.workspaceManagerService.syncStandardObjectsAndFieldsMetadata(
|
|
||||||
dataSourceMetadata.id,
|
dataSourceMetadata.id,
|
||||||
options.workspaceId,
|
options.workspaceId,
|
||||||
);
|
);
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
import { WorkspaceManagerModule } from 'src/workspace/workspace-manager/workspace-manager.module';
|
|
||||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||||
|
import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module';
|
||||||
|
|
||||||
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
|
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [WorkspaceManagerModule, DataSourceModule],
|
imports: [WorkspaceSyncMetadataModule, DataSourceModule],
|
||||||
providers: [SyncWorkspaceMetadataCommand],
|
providers: [SyncWorkspaceMetadataCommand],
|
||||||
})
|
})
|
||||||
export class WorkspaceManagerCommandsModule {}
|
export class WorkspaceSyncMetadataCommandsModule {}
|
||||||
@ -0,0 +1,183 @@
|
|||||||
|
import camelCase from 'lodash.camelcase';
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { generateTargetColumnMap } from 'src/metadata/field-metadata/utils/generate-target-column-map.util';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import { generateDefaultValue } from 'src/metadata/field-metadata/utils/generate-default-value';
|
||||||
|
|
||||||
|
export type FieldMetadataDecorator = {
|
||||||
|
type: FieldMetadataType;
|
||||||
|
label: string;
|
||||||
|
description?: string | null;
|
||||||
|
icon?: string | null;
|
||||||
|
defaultValue?: FieldMetadataDefaultValue | null;
|
||||||
|
joinColumn?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ObjectMetadataDecorator = {
|
||||||
|
namePlural: string;
|
||||||
|
labelSingular: string;
|
||||||
|
labelPlural: string;
|
||||||
|
description?: string | null;
|
||||||
|
icon?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RelationMetadataDecorator = {
|
||||||
|
type: RelationMetadataType;
|
||||||
|
objectName: string;
|
||||||
|
inverseSideFieldName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertClassNameToObjectMetadataName(name: string): string {
|
||||||
|
const classSuffix = 'ObjectMetadata';
|
||||||
|
let objectName = camelCase(name);
|
||||||
|
|
||||||
|
if (objectName.endsWith(classSuffix)) {
|
||||||
|
objectName = objectName.slice(0, -classSuffix.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ObjectMetadata(
|
||||||
|
metadata: ObjectMetadataDecorator,
|
||||||
|
): ClassDecorator {
|
||||||
|
return (target) => {
|
||||||
|
const isSystem = Reflect.getMetadata('isSystem', target) || false;
|
||||||
|
|
||||||
|
const objectName = convertClassNameToObjectMetadataName(target.name);
|
||||||
|
|
||||||
|
Reflect.defineMetadata(
|
||||||
|
'objectMetadata',
|
||||||
|
{
|
||||||
|
nameSingular: objectName,
|
||||||
|
...metadata,
|
||||||
|
targetTableName: objectName,
|
||||||
|
isSystem,
|
||||||
|
isCustom: false,
|
||||||
|
description: metadata.description ?? null,
|
||||||
|
icon: metadata.icon ?? null,
|
||||||
|
},
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IsNullable() {
|
||||||
|
return function (target: object, fieldKey: string) {
|
||||||
|
Reflect.defineMetadata('isNullable', true, target, fieldKey);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IsSystem() {
|
||||||
|
return function (target: object, fieldKey?: string) {
|
||||||
|
if (fieldKey) {
|
||||||
|
Reflect.defineMetadata('isSystem', true, target, fieldKey);
|
||||||
|
} else {
|
||||||
|
Reflect.defineMetadata('isSystem', true, target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FieldMetadata(
|
||||||
|
metadata: FieldMetadataDecorator,
|
||||||
|
): PropertyDecorator {
|
||||||
|
return (target: object, fieldKey: string) => {
|
||||||
|
const existingFieldMetadata =
|
||||||
|
Reflect.getMetadata('fieldMetadata', target.constructor) || {};
|
||||||
|
|
||||||
|
const isNullable =
|
||||||
|
Reflect.getMetadata('isNullable', target, fieldKey) || false;
|
||||||
|
|
||||||
|
const isSystem = Reflect.getMetadata('isSystem', target, fieldKey) || false;
|
||||||
|
|
||||||
|
const { joinColumn, ...fieldMetadata } = metadata;
|
||||||
|
|
||||||
|
Reflect.defineMetadata(
|
||||||
|
'fieldMetadata',
|
||||||
|
{
|
||||||
|
...existingFieldMetadata,
|
||||||
|
[fieldKey]: generateFieldMetadata(
|
||||||
|
fieldMetadata,
|
||||||
|
fieldKey,
|
||||||
|
isNullable,
|
||||||
|
isSystem,
|
||||||
|
),
|
||||||
|
...(joinColumn && fieldMetadata.type === FieldMetadataType.RELATION
|
||||||
|
? {
|
||||||
|
[joinColumn]: generateFieldMetadata(
|
||||||
|
{
|
||||||
|
...fieldMetadata,
|
||||||
|
type: FieldMetadataType.UUID,
|
||||||
|
label: `${fieldMetadata.label} id (foreign key)`,
|
||||||
|
description: `${fieldMetadata.description} id foreign key`,
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
joinColumn,
|
||||||
|
isNullable,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
target.constructor,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFieldMetadata(
|
||||||
|
metadata: FieldMetadataDecorator,
|
||||||
|
fieldKey: string,
|
||||||
|
isNullable: boolean,
|
||||||
|
isSystem: boolean,
|
||||||
|
) {
|
||||||
|
const targetColumnMap = JSON.stringify(
|
||||||
|
generateTargetColumnMap(metadata.type, false, fieldKey),
|
||||||
|
);
|
||||||
|
const defaultValue =
|
||||||
|
metadata.defaultValue ?? generateDefaultValue(metadata.type);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: fieldKey,
|
||||||
|
...metadata,
|
||||||
|
targetColumnMap: targetColumnMap,
|
||||||
|
isNullable,
|
||||||
|
isSystem,
|
||||||
|
isCustom: false,
|
||||||
|
options: null, // TODO: handle options + stringify for the diff.
|
||||||
|
description: metadata.description ?? null,
|
||||||
|
icon: metadata.icon ?? null,
|
||||||
|
defaultValue: defaultValue ? JSON.stringify(defaultValue) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RelationMetadata(
|
||||||
|
metadata: RelationMetadataDecorator,
|
||||||
|
): PropertyDecorator {
|
||||||
|
return (target: object, fieldKey: string) => {
|
||||||
|
const existingRelationMetadata =
|
||||||
|
Reflect.getMetadata('relationMetadata', target.constructor) || [];
|
||||||
|
|
||||||
|
const objectName = convertClassNameToObjectMetadataName(
|
||||||
|
target.constructor.name,
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflect.defineMetadata(
|
||||||
|
'relationMetadata',
|
||||||
|
[
|
||||||
|
...existingRelationMetadata,
|
||||||
|
{
|
||||||
|
type: metadata.type,
|
||||||
|
fromObjectNameSingular: objectName,
|
||||||
|
toObjectNameSingular: metadata.objectName,
|
||||||
|
fromFieldMetadataName: fieldKey,
|
||||||
|
toFieldMetadataName: metadata.inverseSideFieldName ?? objectName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target.constructor,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
FieldMetadata,
|
||||||
|
IsSystem,
|
||||||
|
IsNullable,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'activityTargets',
|
||||||
|
labelSingular: 'Activity Target',
|
||||||
|
labelPlural: 'Activity Targets',
|
||||||
|
description: 'An activity target',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class ActivityTargetObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Activity',
|
||||||
|
description: 'ActivityTarget activity',
|
||||||
|
icon: 'IconNotes',
|
||||||
|
joinColumn: 'activityId',
|
||||||
|
})
|
||||||
|
activity: object;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Person',
|
||||||
|
description: 'ActivityTarget person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'personId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
person: object;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'ActivityTarget company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
company: object;
|
||||||
|
}
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
IsNullable,
|
||||||
|
FieldMetadata,
|
||||||
|
RelationMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'activities',
|
||||||
|
labelSingular: 'Activity',
|
||||||
|
labelPlural: 'Activities',
|
||||||
|
description: 'An activity',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class ActivityObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Title',
|
||||||
|
description: 'Activity title',
|
||||||
|
icon: 'IconNotes',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Body',
|
||||||
|
description: 'Activity body',
|
||||||
|
icon: 'IconList',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
body: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Type',
|
||||||
|
description: 'Activity type',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
defaultValue: { value: 'Note' },
|
||||||
|
})
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.DATE_TIME,
|
||||||
|
label: 'Reminder Date',
|
||||||
|
description: 'Activity reminder date',
|
||||||
|
icon: 'IconCalendarEvent',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
reminderAt: Date;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.DATE_TIME,
|
||||||
|
label: 'Due Date',
|
||||||
|
description: 'Activity due date',
|
||||||
|
icon: 'IconCalendarEvent',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
dueAt: Date;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.DATE_TIME,
|
||||||
|
label: 'Completion Date',
|
||||||
|
description: 'Activity completion date',
|
||||||
|
icon: 'IconCheck',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
completedAt: Date;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Targets',
|
||||||
|
description: 'Activity targets',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'activityTarget',
|
||||||
|
})
|
||||||
|
activityTargets: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Attachments',
|
||||||
|
description: 'Activity attachments',
|
||||||
|
icon: 'IconFileImport',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'attachment',
|
||||||
|
})
|
||||||
|
attachments: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Comments',
|
||||||
|
description: 'Activity comments',
|
||||||
|
icon: 'IconComment',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'comment',
|
||||||
|
})
|
||||||
|
comments: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Author',
|
||||||
|
description: 'Activity author',
|
||||||
|
icon: 'IconUserCircle',
|
||||||
|
joinColumn: 'authorId',
|
||||||
|
})
|
||||||
|
author: object;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Assignee',
|
||||||
|
description: 'Acitivity assignee',
|
||||||
|
icon: 'IconUserCircle',
|
||||||
|
joinColumn: 'assigneeId',
|
||||||
|
})
|
||||||
|
assignee: object;
|
||||||
|
}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import {
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
FieldMetadata,
|
FieldMetadata,
|
||||||
IsNullable,
|
IsNullable,
|
||||||
IsSystem,
|
IsSystem,
|
||||||
ObjectMetadata,
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
} from 'src/workspace/workspace-manager/decorators/metadata.decorator';
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/base.object-metadata';
|
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
namePlural: 'apiKeys',
|
namePlural: 'apiKeys',
|
||||||
labelSingular: 'Api Key',
|
labelSingular: 'Api Key',
|
||||||
labelPlural: 'Api Keys',
|
labelPlural: 'Api Keys',
|
||||||
description: 'A api key',
|
description: 'An api key',
|
||||||
icon: 'IconRobot',
|
icon: 'IconRobot',
|
||||||
})
|
})
|
||||||
@IsSystem()
|
@IsSystem()
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'attachments',
|
||||||
|
labelSingular: 'Attachment',
|
||||||
|
labelPlural: 'Attachments',
|
||||||
|
description: 'An attachment',
|
||||||
|
icon: 'IconFileImport',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class AttachmentObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'Attachment name',
|
||||||
|
icon: 'IconFileUpload',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Full path',
|
||||||
|
description: 'Attachment full path',
|
||||||
|
icon: 'IconLink',
|
||||||
|
})
|
||||||
|
fullPath: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Type',
|
||||||
|
description: 'Attachment type',
|
||||||
|
icon: 'IconList',
|
||||||
|
})
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Author',
|
||||||
|
description: 'Attachment author',
|
||||||
|
icon: 'IconCircleUser',
|
||||||
|
joinColumn: 'authorId',
|
||||||
|
})
|
||||||
|
author: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Activity',
|
||||||
|
description: 'Attachment activity',
|
||||||
|
icon: 'IconNotes',
|
||||||
|
joinColumn: 'activityId',
|
||||||
|
})
|
||||||
|
activity: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Person',
|
||||||
|
description: 'Attachment person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'personId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
person: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'Attachment company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
company: string;
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.en
|
|||||||
import {
|
import {
|
||||||
FieldMetadata,
|
FieldMetadata,
|
||||||
IsSystem,
|
IsSystem,
|
||||||
} from 'src/workspace/workspace-manager/decorators/metadata.decorator';
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
|
||||||
export abstract class BaseObjectMetadata {
|
export abstract class BaseObjectMetadata {
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
@ -22,7 +22,6 @@ export abstract class BaseObjectMetadata {
|
|||||||
icon: 'IconCalendar',
|
icon: 'IconCalendar',
|
||||||
defaultValue: { type: 'now' },
|
defaultValue: { type: 'now' },
|
||||||
})
|
})
|
||||||
@IsSystem()
|
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'comments',
|
||||||
|
labelSingular: 'Comment',
|
||||||
|
labelPlural: 'Comments',
|
||||||
|
description: 'A comment',
|
||||||
|
icon: 'IconMessageCircle',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class CommentObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Body',
|
||||||
|
description: 'Comment body',
|
||||||
|
icon: 'IconLink',
|
||||||
|
defaultValue: { value: '' },
|
||||||
|
})
|
||||||
|
body: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Author',
|
||||||
|
description: 'Comment author',
|
||||||
|
icon: 'IconCircleUser',
|
||||||
|
joinColumn: 'authorId',
|
||||||
|
})
|
||||||
|
author: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Activity',
|
||||||
|
description: 'Comment activity',
|
||||||
|
icon: 'IconNotes',
|
||||||
|
joinColumn: 'activityId',
|
||||||
|
})
|
||||||
|
activity: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
RelationMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'companies',
|
||||||
|
labelSingular: 'Company',
|
||||||
|
labelPlural: 'Companies',
|
||||||
|
description: 'A company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
})
|
||||||
|
export class CompanyObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'The company name',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Domain Name',
|
||||||
|
description:
|
||||||
|
'The company website URL. We use this url to fetch the company icon',
|
||||||
|
icon: 'IconLink',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
domainName?: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Address',
|
||||||
|
description: 'The company address',
|
||||||
|
icon: 'IconMap',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
address: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.NUMBER,
|
||||||
|
label: 'Employees',
|
||||||
|
description: 'Number of employees in the company',
|
||||||
|
icon: 'IconUsers',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
employees: number;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.LINK,
|
||||||
|
label: 'Linkedin',
|
||||||
|
description: 'The company Linkedin account',
|
||||||
|
icon: 'IconBrandLinkedin',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
linkedinLink: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.LINK,
|
||||||
|
label: 'X',
|
||||||
|
description: 'The company Twitter/X account',
|
||||||
|
icon: 'IconBrandX',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
xLink: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.CURRENCY,
|
||||||
|
label: 'ARR',
|
||||||
|
description:
|
||||||
|
'Annual Recurring Revenue: The actual or estimated annual revenue of the company',
|
||||||
|
icon: 'IconMoneybag',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
annualRecurringRevenue: number;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.BOOLEAN,
|
||||||
|
label: 'ICP',
|
||||||
|
description:
|
||||||
|
'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you',
|
||||||
|
icon: 'IconTarget',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
idealCustomerProfile: boolean;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'People',
|
||||||
|
description: 'People linked to the company.',
|
||||||
|
icon: 'IconUsers',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'person',
|
||||||
|
})
|
||||||
|
people: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Account Owner',
|
||||||
|
description:
|
||||||
|
'Your team member responsible for managing the company account',
|
||||||
|
icon: 'IconUserCircle',
|
||||||
|
joinColumn: 'accountOwnerId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
accountOwner: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Activities',
|
||||||
|
description: 'Activities tied to the company',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'activityTarget',
|
||||||
|
})
|
||||||
|
activityTargets: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Opportunities',
|
||||||
|
description: 'Opportunities linked to the company.',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'opportunity',
|
||||||
|
})
|
||||||
|
opportunities: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Favorites',
|
||||||
|
description: 'Favorites linked to the company',
|
||||||
|
icon: 'IconHeart',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'favorite',
|
||||||
|
})
|
||||||
|
favorites: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Attachments',
|
||||||
|
description: 'Attachments linked to the company.',
|
||||||
|
icon: 'IconFileImport',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'attachment',
|
||||||
|
})
|
||||||
|
attachments: object[];
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'favorites',
|
||||||
|
labelSingular: 'Favorite',
|
||||||
|
labelPlural: 'Favorites',
|
||||||
|
description: 'A favorite',
|
||||||
|
icon: 'IconHeart',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class FavoriteObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.NUMBER,
|
||||||
|
label: 'Position',
|
||||||
|
description: 'Favorite position',
|
||||||
|
icon: 'IconList',
|
||||||
|
defaultValue: { value: 0 },
|
||||||
|
})
|
||||||
|
position: number;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Workspace Member',
|
||||||
|
description: 'Favorite workspace member',
|
||||||
|
icon: 'IconCircleUser',
|
||||||
|
joinColumn: 'workspaceMemberId',
|
||||||
|
})
|
||||||
|
workspaceMember: object;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Person',
|
||||||
|
description: 'Favorite person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'personId',
|
||||||
|
})
|
||||||
|
person: object;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'Favorite company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
company: object;
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { ActivityTargetObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity-target.object-metadata';
|
||||||
|
import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata';
|
||||||
|
import { ApiKeyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/api-key.object-metadata';
|
||||||
|
import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata';
|
||||||
|
import { CommentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/comment.object-metadata';
|
||||||
|
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||||
|
import { FavoriteObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/favorite.object-metadata';
|
||||||
|
import { OpportunityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/opportunity.object-metadata';
|
||||||
|
import { PersonObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata';
|
||||||
|
import { PipelineStepObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/pipeline-step.object-metadata';
|
||||||
|
import { ViewFieldObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view-field.object-metadata';
|
||||||
|
import { ViewFilterObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view-filter.object-metadata';
|
||||||
|
import { ViewSortObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view-sort.object-metadata';
|
||||||
|
import { ViewObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata';
|
||||||
|
import { WebhookObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/webook.object-metadata';
|
||||||
|
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
|
||||||
|
|
||||||
|
export const standardObjectMetadata = [
|
||||||
|
ActivityTargetObjectMetadata,
|
||||||
|
ActivityObjectMetadata,
|
||||||
|
ApiKeyObjectMetadata,
|
||||||
|
AttachmentObjectMetadata,
|
||||||
|
CommentObjectMetadata,
|
||||||
|
CompanyObjectMetadata,
|
||||||
|
FavoriteObjectMetadata,
|
||||||
|
OpportunityObjectMetadata,
|
||||||
|
PersonObjectMetadata,
|
||||||
|
PipelineStepObjectMetadata,
|
||||||
|
ViewFieldObjectMetadata,
|
||||||
|
ViewFilterObjectMetadata,
|
||||||
|
ViewSortObjectMetadata,
|
||||||
|
ViewObjectMetadata,
|
||||||
|
WebhookObjectMetadata,
|
||||||
|
WorkspaceMemberObjectMetadata,
|
||||||
|
];
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'opportunities',
|
||||||
|
labelSingular: 'Opportunity',
|
||||||
|
labelPlural: 'Opportunities',
|
||||||
|
description: 'An opportunity',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
})
|
||||||
|
export class OpportunityObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.CURRENCY,
|
||||||
|
label: 'Amount',
|
||||||
|
description: 'Opportunity amount',
|
||||||
|
icon: 'IconCurrencyDollar',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
amount: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.DATE_TIME,
|
||||||
|
label: 'Close date',
|
||||||
|
description: 'Opportunity close date',
|
||||||
|
icon: 'IconCalendarEvent',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
closeDate: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Probability',
|
||||||
|
description: 'Opportunity probability',
|
||||||
|
icon: 'IconProgressCheck',
|
||||||
|
defaultValue: { value: '0' },
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
probability: string;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Pipeline Step',
|
||||||
|
description: 'Opportunity pipeline step',
|
||||||
|
icon: 'IconKanban',
|
||||||
|
joinColumn: 'pipelineStepId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
pipelineStep: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Point of Contact',
|
||||||
|
description: 'Opportunity point of contact',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'pointOfContactId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
pointOfContact: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Person',
|
||||||
|
description: 'Opportunity person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'personId',
|
||||||
|
})
|
||||||
|
person: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'Opportunity company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
company: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
RelationMetadata,
|
||||||
|
IsSystem,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'people',
|
||||||
|
labelSingular: 'Person',
|
||||||
|
labelPlural: 'People',
|
||||||
|
description: 'A person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
})
|
||||||
|
export class PersonObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.FULL_NAME,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'Contact’s name',
|
||||||
|
icon: 'IconUser',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.EMAIL,
|
||||||
|
label: 'Email',
|
||||||
|
description: 'Contact’s Email',
|
||||||
|
icon: 'IconMail',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.LINK,
|
||||||
|
label: 'Linkedin',
|
||||||
|
description: 'Contact’s Linkedin account',
|
||||||
|
icon: 'IconBrandLinkedin',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
linkedinLink: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.LINK,
|
||||||
|
label: 'X',
|
||||||
|
description: 'Contact’s X/Twitter account',
|
||||||
|
icon: 'IconBrandX',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
xLink: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Job Title',
|
||||||
|
description: 'Contact’s job title',
|
||||||
|
icon: 'IconBriefcase',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
jobTitle: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Phone',
|
||||||
|
description: 'Contact’s phone number',
|
||||||
|
icon: 'IconPhone',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'City',
|
||||||
|
description: 'Contact’s city',
|
||||||
|
icon: 'IconMap',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
city: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Avatar',
|
||||||
|
description: 'Contact’s avatar',
|
||||||
|
icon: 'IconFileUpload',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
@IsNullable()
|
||||||
|
avatarUrl: string;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'Contact’s company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
company: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'POC for Opportunities',
|
||||||
|
description: 'Point of Contact for Opportunities',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'opportunity',
|
||||||
|
inverseSideFieldName: 'pointOfContact',
|
||||||
|
})
|
||||||
|
pointOfContactForOpportunities: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Activities',
|
||||||
|
description: 'Activities tied to the contact',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'activityTarget',
|
||||||
|
})
|
||||||
|
activityTargets: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Opportunities',
|
||||||
|
description: 'Opportunities linked to the contact.',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'opportunity',
|
||||||
|
})
|
||||||
|
opportunities: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Favorites',
|
||||||
|
description: 'Favorites linked to the contact',
|
||||||
|
icon: 'IconHeart',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'favorite',
|
||||||
|
})
|
||||||
|
favorites: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Attachments',
|
||||||
|
description: 'Attachments linked to the contact.',
|
||||||
|
icon: 'IconFileImport',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'attachment',
|
||||||
|
})
|
||||||
|
attachments: object[];
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
IsSystem,
|
||||||
|
RelationMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'pipelineSteps',
|
||||||
|
labelSingular: 'Pipeline Step',
|
||||||
|
labelPlural: 'Pipeline Steps',
|
||||||
|
description: 'A pipeline step',
|
||||||
|
icon: 'IconLayoutKanban',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class PipelineStepObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'Pipeline Step name',
|
||||||
|
icon: 'IconCurrencyDollar',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Color',
|
||||||
|
description: 'Pipeline Step color',
|
||||||
|
icon: 'IconColorSwatch',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
color: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.NUMBER,
|
||||||
|
label: 'Position',
|
||||||
|
description: 'Pipeline Step position',
|
||||||
|
icon: 'IconHierarchy2',
|
||||||
|
defaultValue: { value: 0 },
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
position: number;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Opportunities',
|
||||||
|
description: 'Opportunities linked to the step.',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'opportunity',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
opportunities: object[];
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'viewFields',
|
||||||
|
labelSingular: 'View Field',
|
||||||
|
labelPlural: 'View Fields',
|
||||||
|
description: '(System) View Fields',
|
||||||
|
icon: 'IconTag',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class ViewFieldObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.UUID,
|
||||||
|
label: 'Field Metadata Id',
|
||||||
|
description: 'View Field target field',
|
||||||
|
icon: 'IconTag',
|
||||||
|
})
|
||||||
|
fieldMetadataId: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.BOOLEAN,
|
||||||
|
label: 'Visible',
|
||||||
|
description: 'View Field visibility',
|
||||||
|
icon: 'IconEye',
|
||||||
|
defaultValue: { value: true },
|
||||||
|
})
|
||||||
|
isVisible: boolean;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.NUMBER,
|
||||||
|
label: 'Size',
|
||||||
|
description: 'View Field size',
|
||||||
|
icon: 'IconEye',
|
||||||
|
defaultValue: { value: 0 },
|
||||||
|
})
|
||||||
|
size: number;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.NUMBER,
|
||||||
|
label: 'Position',
|
||||||
|
description: 'View Field position',
|
||||||
|
icon: 'IconList',
|
||||||
|
defaultValue: { value: 0 },
|
||||||
|
})
|
||||||
|
position: number;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'View',
|
||||||
|
description: 'View Field related view',
|
||||||
|
icon: 'IconLayoutCollage',
|
||||||
|
joinColumn: 'viewId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
view?: object;
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'viewFilters',
|
||||||
|
labelSingular: 'View Filter',
|
||||||
|
labelPlural: 'View Filters',
|
||||||
|
description: '(System) View Filters',
|
||||||
|
icon: 'IconFilterBolt',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class ViewFilterObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.UUID,
|
||||||
|
label: 'Field Metadata Id',
|
||||||
|
description: 'View Filter target field',
|
||||||
|
icon: null,
|
||||||
|
})
|
||||||
|
fieldMetadataId: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Operand',
|
||||||
|
description: 'View Filter operand',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: 'Contains' },
|
||||||
|
})
|
||||||
|
operand: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Value',
|
||||||
|
description: 'View Filter value',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: '' },
|
||||||
|
})
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Display Value',
|
||||||
|
description: 'View Filter Display Value',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: '' },
|
||||||
|
})
|
||||||
|
displayValue: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'View',
|
||||||
|
description: 'View Filter related view',
|
||||||
|
icon: 'IconLayoutCollage',
|
||||||
|
joinColumn: 'viewId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
view: string;
|
||||||
|
}
|
||||||
@ -4,8 +4,8 @@ import {
|
|||||||
FieldMetadata,
|
FieldMetadata,
|
||||||
IsNullable,
|
IsNullable,
|
||||||
IsSystem,
|
IsSystem,
|
||||||
} from 'src/workspace/workspace-manager/decorators/metadata.decorator';
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/base.object-metadata';
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
namePlural: 'viewSorts',
|
namePlural: 'viewSorts',
|
||||||
@ -20,17 +20,26 @@ export class ViewSortObjectMetadata extends BaseObjectMetadata {
|
|||||||
type: FieldMetadataType.UUID,
|
type: FieldMetadataType.UUID,
|
||||||
label: 'Field Metadata Id',
|
label: 'Field Metadata Id',
|
||||||
description: 'View Sort target field',
|
description: 'View Sort target field',
|
||||||
icon: null,
|
icon: 'IconTag',
|
||||||
})
|
})
|
||||||
fieldMetadataId: string;
|
fieldMetadataId: string;
|
||||||
|
|
||||||
// TODO: We could create a relation decorator but let's keep it simple for now.
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Direction',
|
||||||
|
description: 'View Sort direction',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: 'asc' },
|
||||||
|
})
|
||||||
|
direction: string;
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'View',
|
label: 'View',
|
||||||
description: 'View Sort related view',
|
description: 'View Sort related view',
|
||||||
icon: 'IconLayoutCollage',
|
icon: 'IconLayoutCollage',
|
||||||
|
joinColumn: 'viewId',
|
||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
view?: object;
|
view: string;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
RelationMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'views',
|
||||||
|
labelSingular: 'View',
|
||||||
|
labelPlural: 'Views',
|
||||||
|
description: '(System) Views',
|
||||||
|
icon: 'IconLayoutCollage',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class ViewObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'View name',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: '' },
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.UUID,
|
||||||
|
label: 'Object Metadata Id',
|
||||||
|
description: 'View target object',
|
||||||
|
icon: null,
|
||||||
|
})
|
||||||
|
objectMetadataId: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Type',
|
||||||
|
description: 'View type',
|
||||||
|
icon: null,
|
||||||
|
defaultValue: { value: 'table' },
|
||||||
|
})
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'View Fields',
|
||||||
|
description: 'View Fields',
|
||||||
|
icon: 'IconTag',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'viewField',
|
||||||
|
})
|
||||||
|
viewFields: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'View Filters',
|
||||||
|
description: 'View Filters',
|
||||||
|
icon: 'IconFilterBolt',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'viewFilter',
|
||||||
|
})
|
||||||
|
viewFilters: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'View Sorts',
|
||||||
|
description: 'View Sorts',
|
||||||
|
icon: 'IconArrowsSort',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'viewSort',
|
||||||
|
})
|
||||||
|
viewSorts: object[];
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import {
|
import {
|
||||||
FieldMetadata,
|
|
||||||
IsSystem,
|
|
||||||
ObjectMetadata,
|
ObjectMetadata,
|
||||||
} from 'src/workspace/workspace-manager/decorators/metadata.decorator';
|
IsSystem,
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-manager/standard-objects/base.object-metadata';
|
FieldMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
namePlural: 'webhooks',
|
namePlural: 'webhooks',
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import {
|
||||||
|
ObjectMetadata,
|
||||||
|
IsSystem,
|
||||||
|
FieldMetadata,
|
||||||
|
IsNullable,
|
||||||
|
RelationMetadata,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'workspaceMembers',
|
||||||
|
labelSingular: 'Workspace Member',
|
||||||
|
labelPlural: 'Workspace Members',
|
||||||
|
description: 'A workspace member',
|
||||||
|
icon: 'IconUserCircle',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.FULL_NAME,
|
||||||
|
label: 'Name',
|
||||||
|
description: 'Workspace member name',
|
||||||
|
icon: 'IconCircleUser',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Color Scheme',
|
||||||
|
description: 'Preferred color scheme',
|
||||||
|
icon: 'IconColorSwatch',
|
||||||
|
defaultValue: { value: 'Light' },
|
||||||
|
})
|
||||||
|
colorScheme: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Language',
|
||||||
|
description: 'Preferred language',
|
||||||
|
icon: 'IconLanguage',
|
||||||
|
defaultValue: { value: 'en' },
|
||||||
|
})
|
||||||
|
locale: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Avatar Url',
|
||||||
|
description: 'Workspace member avatar',
|
||||||
|
icon: 'IconFileUpload',
|
||||||
|
defaultValue: { value: '' },
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
avatarUrl: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.UUID,
|
||||||
|
label: 'User Id',
|
||||||
|
description: 'Associated User Id',
|
||||||
|
icon: 'IconCircleUsers',
|
||||||
|
})
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Authored activities',
|
||||||
|
description: 'Activities created by the workspace member',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'activity',
|
||||||
|
inverseSideFieldName: 'author',
|
||||||
|
})
|
||||||
|
authoredActivities: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Assigned activities',
|
||||||
|
description: 'Activities assigned to the workspace member',
|
||||||
|
icon: 'IconCheckbox',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'activity',
|
||||||
|
inverseSideFieldName: 'assignee',
|
||||||
|
})
|
||||||
|
assignedActivities: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Favorites',
|
||||||
|
description: 'Favorites linked to the workspace member',
|
||||||
|
icon: 'IconHeart',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'favorite',
|
||||||
|
})
|
||||||
|
favorites: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Account Owner For Companies',
|
||||||
|
description: 'Account owner for companies',
|
||||||
|
icon: 'IconBriefcase',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'company',
|
||||||
|
inverseSideFieldName: 'accountOwner',
|
||||||
|
})
|
||||||
|
accountOwnerForCompanies: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Authored attachments',
|
||||||
|
description: 'Attachments created by the workspace member',
|
||||||
|
icon: 'IconFileImport',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'attachment',
|
||||||
|
inverseSideFieldName: 'author',
|
||||||
|
})
|
||||||
|
authoredAttachments: object[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Authored comments',
|
||||||
|
description: 'Authored comments',
|
||||||
|
icon: 'IconComment',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'comment',
|
||||||
|
inverseSideFieldName: 'author',
|
||||||
|
})
|
||||||
|
authoredComments: object[];
|
||||||
|
}
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
|
||||||
|
export class MetadataParser {
|
||||||
|
static parseMetadata(
|
||||||
|
metadata: typeof BaseObjectMetadata,
|
||||||
|
workspaceId: string,
|
||||||
|
dataSourceId: string,
|
||||||
|
) {
|
||||||
|
const objectMetadata = Reflect.getMetadata('objectMetadata', metadata);
|
||||||
|
const fieldMetadata = Reflect.getMetadata('fieldMetadata', metadata);
|
||||||
|
|
||||||
|
if (objectMetadata) {
|
||||||
|
const fields = Object.values(fieldMetadata);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...objectMetadata,
|
||||||
|
workspaceId,
|
||||||
|
dataSourceId,
|
||||||
|
fields: fields.map((field: FieldMetadataEntity) => ({
|
||||||
|
...field,
|
||||||
|
workspaceId,
|
||||||
|
isSystem: objectMetadata.isSystem || field.isSystem,
|
||||||
|
defaultValue: field.defaultValue || null,
|
||||||
|
options: field.options || null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseAllMetadata(
|
||||||
|
metadata: (typeof BaseObjectMetadata)[],
|
||||||
|
workspaceId: string,
|
||||||
|
dataSourceId: string,
|
||||||
|
) {
|
||||||
|
return metadata.map((_metadata) =>
|
||||||
|
MetadataParser.parseMetadata(_metadata, workspaceId, dataSourceId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseRelationMetadata(
|
||||||
|
metadata: typeof BaseObjectMetadata,
|
||||||
|
workspaceId: string,
|
||||||
|
objectMetadataFromDB: Record<string, ObjectMetadataEntity>,
|
||||||
|
) {
|
||||||
|
const objectMetadata = Reflect.getMetadata('objectMetadata', metadata);
|
||||||
|
const relationMetadata = Reflect.getMetadata('relationMetadata', metadata);
|
||||||
|
|
||||||
|
if (!relationMetadata) return [];
|
||||||
|
|
||||||
|
return relationMetadata.map((relation) => {
|
||||||
|
const fromObjectMetadata =
|
||||||
|
objectMetadataFromDB[relation.fromObjectNameSingular];
|
||||||
|
assert(
|
||||||
|
fromObjectMetadata,
|
||||||
|
`Object ${relation.fromObjectNameSingular} not found in DB
|
||||||
|
for relation defined in class ${objectMetadata.nameSingular}`,
|
||||||
|
);
|
||||||
|
const toObjectMetadata =
|
||||||
|
objectMetadataFromDB[relation.toObjectNameSingular];
|
||||||
|
assert(
|
||||||
|
toObjectMetadata,
|
||||||
|
`Object ${relation.toObjectNameSingular} not found in DB
|
||||||
|
for relation defined in class ${objectMetadata.nameSingular}`,
|
||||||
|
);
|
||||||
|
const fromFieldMetadata =
|
||||||
|
fromObjectMetadata?.fields[relation.fromFieldMetadataName];
|
||||||
|
assert(
|
||||||
|
fromFieldMetadata,
|
||||||
|
`Field ${relation.fromFieldMetadataName} not found in object ${relation.fromObjectNameSingular}
|
||||||
|
for relation defined in class ${objectMetadata.nameSingular}`,
|
||||||
|
);
|
||||||
|
const toFieldMetadata =
|
||||||
|
toObjectMetadata?.fields[relation.toFieldMetadataName];
|
||||||
|
assert(
|
||||||
|
toFieldMetadata,
|
||||||
|
`Field ${relation.toFieldMetadataName} not found in object ${relation.toObjectNameSingular}
|
||||||
|
for relation defined in class ${objectMetadata.nameSingular}`,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
relationType: relation.type,
|
||||||
|
fromObjectMetadataId: fromObjectMetadata?.id,
|
||||||
|
toObjectMetadataId: toObjectMetadata?.id,
|
||||||
|
fromFieldMetadataId: fromFieldMetadata?.id,
|
||||||
|
toFieldMetadataId: toFieldMetadata?.id,
|
||||||
|
workspaceId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseAllRelations(
|
||||||
|
metadata: (typeof BaseObjectMetadata)[],
|
||||||
|
workspaceId: string,
|
||||||
|
objectMetadataFromDB: Record<string, ObjectMetadataEntity>,
|
||||||
|
) {
|
||||||
|
return metadata.flatMap((_metadata) =>
|
||||||
|
MetadataParser.parseRelationMetadata(
|
||||||
|
_metadata,
|
||||||
|
workspaceId,
|
||||||
|
objectMetadataFromDB,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,9 +11,12 @@ import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metada
|
|||||||
export const filterIgnoredProperties = (
|
export const filterIgnoredProperties = (
|
||||||
obj: any,
|
obj: any,
|
||||||
propertiesToIgnore: string[],
|
propertiesToIgnore: string[],
|
||||||
|
mapFunction?: (value: any) => any,
|
||||||
) => {
|
) => {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(obj).filter(([key]) => !propertiesToIgnore.includes(key)),
|
Object.entries(obj)
|
||||||
|
.filter(([key]) => !propertiesToIgnore.includes(key))
|
||||||
|
.map(([key, value]) => [key, mapFunction ? mapFunction(value) : value]),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,3 +44,22 @@ export const mapObjectMetadataByUniqueIdentifier = (
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const convertStringifiedFieldsToJSON = (fieldMetadata) => {
|
||||||
|
if (fieldMetadata.targetColumnMap) {
|
||||||
|
fieldMetadata.targetColumnMap = JSON.parse(
|
||||||
|
fieldMetadata.targetColumnMap as unknown as string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (fieldMetadata.defaultValue) {
|
||||||
|
fieldMetadata.defaultValue = JSON.parse(
|
||||||
|
fieldMetadata.defaultValue as unknown as string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (fieldMetadata.options) {
|
||||||
|
fieldMetadata.options = JSON.parse(
|
||||||
|
fieldMetadata.options as unknown as string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return fieldMetadata;
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
|
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import { WorkspaceMigrationEntity } from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||||
|
import { WorkspaceMigrationRunnerModule } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.module';
|
||||||
|
import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync.metadata.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
WorkspaceMigrationModule,
|
||||||
|
WorkspaceMigrationRunnerModule,
|
||||||
|
TypeOrmModule.forFeature(
|
||||||
|
[
|
||||||
|
FieldMetadataEntity,
|
||||||
|
ObjectMetadataEntity,
|
||||||
|
RelationMetadataEntity,
|
||||||
|
WorkspaceMigrationEntity,
|
||||||
|
],
|
||||||
|
'metadata',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
exports: [WorkspaceSyncMetadataService],
|
||||||
|
providers: [WorkspaceSyncMetadataService],
|
||||||
|
})
|
||||||
|
export class WorkspaceSyncMetadataModule {}
|
||||||
@ -0,0 +1,435 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import diff from 'microdiff';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import camelCase from 'lodash.camelcase';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FieldMetadataEntity,
|
||||||
|
FieldMetadataType,
|
||||||
|
} from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
|
import {
|
||||||
|
RelationMetadataEntity,
|
||||||
|
RelationMetadataType,
|
||||||
|
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
import { MetadataParser } from 'src/workspace/workspace-sync-metadata/utils/metadata.parser';
|
||||||
|
import {
|
||||||
|
mapObjectMetadataByUniqueIdentifier,
|
||||||
|
filterIgnoredProperties,
|
||||||
|
convertStringifiedFieldsToJSON,
|
||||||
|
} from 'src/workspace/workspace-sync-metadata/utils/sync-metadata.util';
|
||||||
|
import { standardObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationColumnRelation,
|
||||||
|
WorkspaceMigrationEntity,
|
||||||
|
WorkspaceMigrationTableAction,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
|
||||||
|
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceSyncMetadataService {
|
||||||
|
constructor(
|
||||||
|
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||||
|
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||||
|
|
||||||
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
|
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||||
|
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||||
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
@InjectRepository(RelationMetadataEntity, 'metadata')
|
||||||
|
private readonly relationMetadataRepository: Repository<RelationMetadataEntity>,
|
||||||
|
@InjectRepository(WorkspaceMigrationEntity, 'metadata')
|
||||||
|
private readonly workspaceMigrationRepository: Repository<WorkspaceMigrationEntity>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Sync all standard objects and fields metadata for a given workspace and data source
|
||||||
|
* This will update the metadata if it has changed and generate migrations based on the diff.
|
||||||
|
*
|
||||||
|
* @param dataSourceId
|
||||||
|
* @param workspaceId
|
||||||
|
*/
|
||||||
|
public async syncStandardObjectsAndFieldsMetadata(
|
||||||
|
dataSourceId: string,
|
||||||
|
workspaceId: string,
|
||||||
|
) {
|
||||||
|
const standardObjects = MetadataParser.parseAllMetadata(
|
||||||
|
standardObjectMetadata,
|
||||||
|
workspaceId,
|
||||||
|
dataSourceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const objectsInDB = await this.objectMetadataRepository.find({
|
||||||
|
where: { workspaceId, dataSourceId, isCustom: false },
|
||||||
|
relations: ['fields'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const objectsInDBByName =
|
||||||
|
mapObjectMetadataByUniqueIdentifier(objectsInDB);
|
||||||
|
const standardObjectsByName =
|
||||||
|
mapObjectMetadataByUniqueIdentifier(standardObjects);
|
||||||
|
|
||||||
|
const objectsToCreate: ObjectMetadataEntity[] = [];
|
||||||
|
const objectsToDelete = objectsInDB.filter(
|
||||||
|
(objectInDB) => !standardObjectsByName[objectInDB.nameSingular],
|
||||||
|
);
|
||||||
|
const objectsToUpdate: Record<string, ObjectMetadataEntity> = {};
|
||||||
|
|
||||||
|
const fieldsToCreate: FieldMetadataEntity[] = [];
|
||||||
|
const fieldsToDelete: FieldMetadataEntity[] = [];
|
||||||
|
const fieldsToUpdate: Record<string, FieldMetadataEntity> = {};
|
||||||
|
|
||||||
|
for (const standardObjectName in standardObjectsByName) {
|
||||||
|
const standardObject = standardObjectsByName[standardObjectName];
|
||||||
|
const objectInDB = objectsInDBByName[standardObjectName];
|
||||||
|
|
||||||
|
if (!objectInDB) {
|
||||||
|
objectsToCreate.push(standardObject);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deconstruct fields and compare objects and fields independently
|
||||||
|
const { fields: objectInDBFields, ...objectInDBWithoutFields } =
|
||||||
|
objectInDB;
|
||||||
|
const { fields: standardObjectFields, ...standardObjectWithoutFields } =
|
||||||
|
standardObject;
|
||||||
|
|
||||||
|
const objectPropertiesToIgnore = [
|
||||||
|
'id',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'labelIdentifierFieldMetadataId',
|
||||||
|
'imageIdentifierFieldMetadataId',
|
||||||
|
'isActive',
|
||||||
|
];
|
||||||
|
const objectDiffWithoutIgnoredProperties = filterIgnoredProperties(
|
||||||
|
objectInDBWithoutFields,
|
||||||
|
objectPropertiesToIgnore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldPropertiesToIgnore = [
|
||||||
|
'id',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'objectMetadataId',
|
||||||
|
'isActive',
|
||||||
|
];
|
||||||
|
const objectInDBFieldsWithoutDefaultFields = Object.fromEntries(
|
||||||
|
Object.entries(objectInDBFields).map(([key, value]) => {
|
||||||
|
if (value === null || typeof value !== 'object') {
|
||||||
|
return [key, value];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
key,
|
||||||
|
filterIgnoredProperties(
|
||||||
|
value,
|
||||||
|
fieldPropertiesToIgnore,
|
||||||
|
(property) => {
|
||||||
|
if (property !== null && typeof property === 'object') {
|
||||||
|
return JSON.stringify(property);
|
||||||
|
}
|
||||||
|
return property;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compare objects
|
||||||
|
const objectDiff = diff(
|
||||||
|
objectDiffWithoutIgnoredProperties,
|
||||||
|
standardObjectWithoutFields,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compare fields
|
||||||
|
const fieldsDiff = diff(
|
||||||
|
objectInDBFieldsWithoutDefaultFields,
|
||||||
|
standardObjectFields,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const diff of objectDiff) {
|
||||||
|
// We only handle CHANGE here as REMOVE and CREATE are handled earlier.
|
||||||
|
if (diff.type === 'CHANGE') {
|
||||||
|
const property = diff.path[0];
|
||||||
|
objectsToUpdate[objectInDB.id] = {
|
||||||
|
...objectsToUpdate[objectInDB.id],
|
||||||
|
[property]: diff.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const diff of fieldsDiff) {
|
||||||
|
const fieldName = diff.path[0];
|
||||||
|
if (diff.type === 'CREATE')
|
||||||
|
fieldsToCreate.push({
|
||||||
|
...standardObjectFields[fieldName],
|
||||||
|
objectMetadataId: objectInDB.id,
|
||||||
|
});
|
||||||
|
if (diff.type === 'REMOVE' && diff.path.length === 1)
|
||||||
|
fieldsToDelete.push(objectInDBFields[fieldName]);
|
||||||
|
if (diff.type === 'CHANGE') {
|
||||||
|
const property = diff.path[diff.path.length - 1];
|
||||||
|
fieldsToUpdate[objectInDBFields[fieldName].id] = {
|
||||||
|
...fieldsToUpdate[objectInDBFields[fieldName].id],
|
||||||
|
[property]: diff.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE OBJECTS
|
||||||
|
await this.objectMetadataRepository.save(
|
||||||
|
objectsToCreate.map((object) => ({
|
||||||
|
...object,
|
||||||
|
isActive: true,
|
||||||
|
fields: Object.values(object.fields).map((field) => ({
|
||||||
|
...convertStringifiedFieldsToJSON(field),
|
||||||
|
isActive: true,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
// UPDATE OBJECTS, this is not optimal as we are running n queries here.
|
||||||
|
for (const [key, value] of Object.entries(objectsToUpdate)) {
|
||||||
|
await this.objectMetadataRepository.update(key, value);
|
||||||
|
}
|
||||||
|
// DELETE OBJECTS
|
||||||
|
if (objectsToDelete.length > 0) {
|
||||||
|
await this.objectMetadataRepository.delete(
|
||||||
|
objectsToDelete.map((object) => object.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE FIELDS
|
||||||
|
await this.fieldMetadataRepository.save(
|
||||||
|
fieldsToCreate.map((field) => convertStringifiedFieldsToJSON(field)),
|
||||||
|
);
|
||||||
|
// UPDATE FIELDS
|
||||||
|
for (const [key, value] of Object.entries(fieldsToUpdate)) {
|
||||||
|
await this.fieldMetadataRepository.update(
|
||||||
|
key,
|
||||||
|
convertStringifiedFieldsToJSON(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// DELETE FIELDS
|
||||||
|
// TODO: handle relation fields deletion. We need to delete the relation metadata first due to the DB constraint.
|
||||||
|
const fieldsToDeleteWithoutRelationType = fieldsToDelete.filter(
|
||||||
|
(field) => field.type !== FieldMetadataType.RELATION,
|
||||||
|
);
|
||||||
|
if (fieldsToDeleteWithoutRelationType.length > 0) {
|
||||||
|
await this.fieldMetadataRepository.delete(
|
||||||
|
fieldsToDeleteWithoutRelationType.map((field) => field.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate migrations
|
||||||
|
await this.generateMigrationsFromSync(
|
||||||
|
objectsToCreate,
|
||||||
|
objectsToDelete,
|
||||||
|
fieldsToCreate,
|
||||||
|
fieldsToDelete,
|
||||||
|
);
|
||||||
|
|
||||||
|
// We run syncRelationMetadata after everything to ensure that all objects and fields are
|
||||||
|
// in the DB before creating relations.
|
||||||
|
await this.syncRelationMetadata(workspaceId, dataSourceId);
|
||||||
|
|
||||||
|
// Execute migrations
|
||||||
|
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sync of standard objects failed with:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async syncRelationMetadata(
|
||||||
|
workspaceId: string,
|
||||||
|
dataSourceId: string,
|
||||||
|
) {
|
||||||
|
const objectsInDB = await this.objectMetadataRepository.find({
|
||||||
|
where: { workspaceId, dataSourceId, isCustom: false },
|
||||||
|
relations: ['fields'],
|
||||||
|
});
|
||||||
|
const objectsInDBByName = mapObjectMetadataByUniqueIdentifier(objectsInDB);
|
||||||
|
const standardRelations = MetadataParser.parseAllRelations(
|
||||||
|
standardObjectMetadata,
|
||||||
|
workspaceId,
|
||||||
|
objectsInDBByName,
|
||||||
|
).reduce((result, currentObject) => {
|
||||||
|
const key = `${currentObject.fromObjectMetadataId}->${currentObject.fromFieldMetadataId}`;
|
||||||
|
result[key] = currentObject;
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// TODO: filter out custom relations once isCustom has been added to relationMetadata table
|
||||||
|
const relationsInDB = await this.relationMetadataRepository.find({
|
||||||
|
where: { workspaceId },
|
||||||
|
});
|
||||||
|
|
||||||
|
// We filter out 'id' later because we need it to remove the relation from DB
|
||||||
|
const relationsInDBWithoutIgnoredProperties = relationsInDB
|
||||||
|
.map((relation) =>
|
||||||
|
filterIgnoredProperties(relation, ['createdAt', 'updatedAt']),
|
||||||
|
)
|
||||||
|
.reduce((result, currentObject) => {
|
||||||
|
const key = `${currentObject.fromObjectMetadataId}->${currentObject.fromFieldMetadataId}`;
|
||||||
|
result[key] = currentObject;
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Compare relations
|
||||||
|
const relationsDiff = diff(
|
||||||
|
relationsInDBWithoutIgnoredProperties,
|
||||||
|
standardRelations,
|
||||||
|
);
|
||||||
|
|
||||||
|
const relationsToCreate: RelationMetadataEntity[] = [];
|
||||||
|
const relationsToDelete: RelationMetadataEntity[] = [];
|
||||||
|
|
||||||
|
for (const diff of relationsDiff) {
|
||||||
|
if (diff.type === 'CREATE') {
|
||||||
|
relationsToCreate.push(diff.value);
|
||||||
|
}
|
||||||
|
if (diff.type === 'REMOVE' && diff.path[diff.path.length - 1] !== 'id') {
|
||||||
|
relationsToDelete.push(diff.oldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// CREATE RELATIONS
|
||||||
|
await this.relationMetadataRepository.save(relationsToCreate);
|
||||||
|
// DELETE RELATIONS
|
||||||
|
if (relationsToDelete.length > 0) {
|
||||||
|
await this.relationMetadataRepository.delete(
|
||||||
|
relationsToDelete.map((relation) => relation.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.generateRelationMigrationsFromSync(
|
||||||
|
relationsToCreate,
|
||||||
|
relationsToDelete,
|
||||||
|
objectsInDB,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sync of standard relations failed with:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateMigrationsFromSync(
|
||||||
|
objectsToCreate: ObjectMetadataEntity[],
|
||||||
|
_objectsToDelete: ObjectMetadataEntity[],
|
||||||
|
_fieldsToCreate: FieldMetadataEntity[],
|
||||||
|
_fieldsToDelete: FieldMetadataEntity[],
|
||||||
|
) {
|
||||||
|
const migrationsToSave: Partial<WorkspaceMigrationEntity>[] = [];
|
||||||
|
|
||||||
|
if (objectsToCreate.length > 0) {
|
||||||
|
objectsToCreate.map((object) => {
|
||||||
|
const migrations = [
|
||||||
|
{
|
||||||
|
name: object.targetTableName,
|
||||||
|
action: 'create',
|
||||||
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
|
...Object.values(object.fields)
|
||||||
|
.filter((field) => field.type !== FieldMetadataType.RELATION)
|
||||||
|
.map(
|
||||||
|
(field) =>
|
||||||
|
({
|
||||||
|
name: object.targetTableName,
|
||||||
|
action: 'alter',
|
||||||
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
|
WorkspaceMigrationColumnActionType.CREATE,
|
||||||
|
field,
|
||||||
|
),
|
||||||
|
} satisfies WorkspaceMigrationTableAction),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
migrationsToSave.push({
|
||||||
|
workspaceId: object.workspaceId,
|
||||||
|
isCustom: false,
|
||||||
|
migrations,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.workspaceMigrationRepository.save(migrationsToSave);
|
||||||
|
|
||||||
|
// TODO: handle delete migrations
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateRelationMigrationsFromSync(
|
||||||
|
relationsToCreate: RelationMetadataEntity[],
|
||||||
|
_relationsToDelete: RelationMetadataEntity[],
|
||||||
|
objectsInDB: ObjectMetadataEntity[],
|
||||||
|
) {
|
||||||
|
const relationsMigrationsToSave: Partial<WorkspaceMigrationEntity>[] = [];
|
||||||
|
|
||||||
|
if (relationsToCreate.length > 0) {
|
||||||
|
relationsToCreate.map((relation) => {
|
||||||
|
const toObjectMetadata = objectsInDB.find(
|
||||||
|
(object) => object.id === relation.toObjectMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fromObjectMetadata = objectsInDB.find(
|
||||||
|
(object) => object.id === relation.fromObjectMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!toObjectMetadata) {
|
||||||
|
throw new Error(
|
||||||
|
`ObjectMetadata with id ${relation.toObjectMetadataId} not found`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromObjectMetadata) {
|
||||||
|
throw new Error(
|
||||||
|
`ObjectMetadata with id ${relation.fromObjectMetadataId} not found`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toFieldMetadata = toObjectMetadata.fields.find(
|
||||||
|
(field) => field.id === relation.toFieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!toFieldMetadata) {
|
||||||
|
throw new Error(
|
||||||
|
`FieldMetadata with id ${relation.toFieldMetadataId} not found`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrations = [
|
||||||
|
{
|
||||||
|
name: toObjectMetadata.targetTableName,
|
||||||
|
action: 'alter',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||||
|
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||||
|
referencedTableName: fromObjectMetadata.targetTableName,
|
||||||
|
referencedTableColumnName: 'id',
|
||||||
|
isUnique:
|
||||||
|
relation.relationType === RelationMetadataType.ONE_TO_ONE,
|
||||||
|
} satisfies WorkspaceMigrationColumnRelation,
|
||||||
|
],
|
||||||
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
|
];
|
||||||
|
|
||||||
|
relationsMigrationsToSave.push({
|
||||||
|
workspaceId: relation.workspaceId,
|
||||||
|
isCustom: false,
|
||||||
|
migrations,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.workspaceMigrationRepository.save(relationsMigrationsToSave);
|
||||||
|
|
||||||
|
// TODO: handle delete migrations
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user