Migrate to a monorepo structure (#2909)

This commit is contained in:
Charles Bochet
2023-12-10 18:10:54 +01:00
committed by GitHub
parent a70a9281eb
commit 5bdca9de6c
2304 changed files with 37152 additions and 25869 deletions

View File

@ -0,0 +1,92 @@
import { Injectable, Logger } from '@nestjs/common';
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnAlter,
WorkspaceMigrationColumnCreate,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value';
import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util';
import { ColumnActionAbstractFactory } from 'src/metadata/workspace-migration/factories/column-action-abstract.factory';
export type BasicFieldMetadataType =
| FieldMetadataType.UUID
| FieldMetadataType.TEXT
| FieldMetadataType.PHONE
| FieldMetadataType.EMAIL
| FieldMetadataType.NUMERIC
| FieldMetadataType.NUMBER
| FieldMetadataType.PROBABILITY
| FieldMetadataType.BOOLEAN
| FieldMetadataType.DATE_TIME;
@Injectable()
export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicFieldMetadataType> {
protected readonly logger = new Logger(BasicColumnActionFactory.name);
protected handleCreateAction(
fieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnCreate {
const defaultValue =
this.getDefaultValue(fieldMetadata.defaultValue) ?? options?.defaultValue;
const serializedDefaultValue = serializeDefaultValue(defaultValue);
return {
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
isNullable: fieldMetadata.isNullable,
defaultValue: serializedDefaultValue,
};
}
protected handleAlterAction(
currentFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
alteredFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnAlter {
const defaultValue =
this.getDefaultValue(alteredFieldMetadata.defaultValue) ??
options?.defaultValue;
const serializedDefaultValue = serializeDefaultValue(defaultValue);
return {
action: WorkspaceMigrationColumnActionType.ALTER,
currentColumnDefinition: {
columnName: currentFieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
isNullable: currentFieldMetadata.isNullable,
defaultValue: serializeDefaultValue(
this.getDefaultValue(currentFieldMetadata.defaultValue),
),
},
alteredColumnDefinition: {
columnName: alteredFieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
isNullable: alteredFieldMetadata.isNullable,
defaultValue: serializedDefaultValue,
},
};
}
private getDefaultValue(
defaultValue:
| FieldMetadataDefaultValue<BasicFieldMetadataType>
| undefined
| null,
) {
if (!defaultValue) return null;
if ('type' in defaultValue) {
return defaultValue;
} else {
return defaultValue?.value;
}
}
}

View File

@ -0,0 +1,66 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Logger } from '@nestjs/common';
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
import { WorkspaceColumnActionFactory } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-factory.interface';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnAction,
WorkspaceMigrationColumnCreate,
WorkspaceMigrationColumnAlter,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export class ColumnActionAbstractFactory<
T extends FieldMetadataType | 'default',
> implements WorkspaceColumnActionFactory<T>
{
protected readonly logger = new Logger(ColumnActionAbstractFactory.name);
create(
action:
| WorkspaceMigrationColumnActionType.CREATE
| WorkspaceMigrationColumnActionType.ALTER,
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
alteredFieldMetadata: FieldMetadataInterface<T>,
options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnAction {
switch (action) {
case WorkspaceMigrationColumnActionType.CREATE:
return this.handleCreateAction(alteredFieldMetadata, options);
case WorkspaceMigrationColumnActionType.ALTER: {
if (!currentFieldMetadata) {
throw new Error('current field metadata is required for alter');
}
return this.handleAlterAction(
currentFieldMetadata,
alteredFieldMetadata,
options,
);
}
default: {
this.logger.error(`Invalid action: ${action}`);
throw new Error('[AbstractFactory]: invalid action');
}
}
}
protected handleCreateAction(
_fieldMetadata: FieldMetadataInterface<T>,
_options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnCreate {
throw new Error('handleCreateAction method not implemented.');
}
protected handleAlterAction(
_currentFieldMetadata: FieldMetadataInterface<T>,
_alteredFieldMetadata: FieldMetadataInterface<T>,
_options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnAlter {
throw new Error('handleAlterAction method not implemented.');
}
}

View File

@ -0,0 +1,99 @@
import { Injectable, Logger } from '@nestjs/common';
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnAlter,
WorkspaceMigrationColumnCreate,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value';
import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util';
import { ColumnActionAbstractFactory } from 'src/metadata/workspace-migration/factories/column-action-abstract.factory';
export type EnumFieldMetadataType =
| FieldMetadataType.RATING
| FieldMetadataType.SELECT
| FieldMetadataType.MULTI_SELECT;
@Injectable()
export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFieldMetadataType> {
protected readonly logger = new Logger(EnumColumnActionFactory.name);
protected handleCreateAction(
fieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
options: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnCreate {
const defaultValue =
fieldMetadata.defaultValue?.value ?? options?.defaultValue;
const serializedDefaultValue = serializeDefaultValue(defaultValue);
const enumOptions = fieldMetadata.options
? [...fieldMetadata.options.map((option) => option.value)]
: undefined;
return {
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
enum: enumOptions,
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
isNullable: fieldMetadata.isNullable,
defaultValue: serializedDefaultValue,
};
}
protected handleAlterAction(
currentFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
alteredFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
options: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnAlter {
const defaultValue =
alteredFieldMetadata.defaultValue?.value ?? options?.defaultValue;
const serializedDefaultValue = serializeDefaultValue(defaultValue);
const enumOptions = alteredFieldMetadata.options
? [
...alteredFieldMetadata.options.map((option) => {
const currentOption = currentFieldMetadata.options?.find(
(currentOption) => currentOption.id === option.id,
);
// The id is the same, but the value is different, so we need to alter the enum
if (currentOption && currentOption.value !== option.value) {
return {
from: currentOption.value,
to: option.value,
};
}
return option.value;
}),
]
: undefined;
return {
action: WorkspaceMigrationColumnActionType.ALTER,
currentColumnDefinition: {
columnName: currentFieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
enum: currentFieldMetadata.options
? [...currentFieldMetadata.options.map((option) => option.value)]
: undefined,
isArray: currentFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
isNullable: currentFieldMetadata.isNullable,
defaultValue: serializeDefaultValue(
currentFieldMetadata.defaultValue?.value,
),
},
alteredColumnDefinition: {
columnName: alteredFieldMetadata.targetColumnMap.value,
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
enum: enumOptions,
isArray: alteredFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
isNullable: alteredFieldMetadata.isNullable,
defaultValue: serializedDefaultValue,
},
};
}
}

View File

@ -0,0 +1,7 @@
import { BasicColumnActionFactory } from 'src/metadata/workspace-migration/factories/basic-column-action.factory';
import { EnumColumnActionFactory } from 'src/metadata/workspace-migration/factories/enum-column-action.factory';
export const workspaceColumnActionFactories = [
BasicColumnActionFactory,
EnumColumnActionFactory,
];

View File

@ -0,0 +1,21 @@
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export interface WorkspaceColumnActionFactory<
T extends FieldMetadataType | 'default',
> {
create(
action:
| WorkspaceMigrationColumnActionType.CREATE
| WorkspaceMigrationColumnActionType.ALTER,
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
alteredFieldMetadata: FieldMetadataInterface<T>,
options?: WorkspaceColumnActionOptions,
): WorkspaceMigrationColumnAction;
}

View File

@ -0,0 +1,3 @@
export interface WorkspaceColumnActionOptions {
defaultValue?: string;
}

View File

@ -0,0 +1,84 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addCompanyTable: WorkspaceMigrationTableAction[] = [
{
name: 'company',
action: 'create',
},
{
name: 'company',
action: 'alter',
columns: [
{
columnName: 'name',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'domainName',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'address',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'employees',
columnType: 'integer',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'linkedinLinkUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'linkedinLinkLabel',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'xLinkUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'xLinkLabel',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'annualRecurringRevenueAmountMicros',
columnType: 'numeric',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'annualRecurringRevenueCurrencyCode',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'idealCustomerProfile',
columnType: 'boolean',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'accountOwnerId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,34 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addViewTable: WorkspaceMigrationTableAction[] = [
{
name: 'view',
action: 'create',
},
{
name: 'view',
action: 'alter',
columns: [
{
columnName: 'name',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'objectMetadataId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'type',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'table'",
},
],
},
];

View File

@ -0,0 +1,51 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addViewFieldTable: WorkspaceMigrationTableAction[] = [
{
name: 'viewField',
action: 'create',
},
{
name: 'viewField',
action: 'alter',
columns: [
{
columnName: 'fieldMetadataId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'position',
columnType: 'float',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: 0,
},
{
columnName: 'isVisible',
columnType: 'boolean',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: true,
},
{
columnName: 'size',
columnType: 'integer',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: 0,
},
{
columnName: 'viewId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'viewId',
referencedTableName: 'view',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,51 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addViewFilterTable: WorkspaceMigrationTableAction[] = [
{
name: 'viewFilter',
action: 'create',
},
{
name: 'viewFilter',
action: 'alter',
columns: [
{
columnName: 'fieldMetadataId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'operand',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'Contains'",
},
{
columnName: 'value',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'displayValue',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'viewId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'viewId',
referencedTableName: 'view',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,39 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addViewSortTable: WorkspaceMigrationTableAction[] = [
{
name: 'viewSort',
action: 'create',
},
{
name: 'viewSort',
action: 'alter',
columns: [
{
columnName: 'fieldMetadataId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'direction',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'asc'",
},
{
columnName: 'viewId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'viewId',
referencedTableName: 'view',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,32 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addActivityTargetTable: WorkspaceMigrationTableAction[] = [
{
name: 'activityTarget',
action: 'create',
},
{
name: 'activityTarget',
action: 'alter',
columns: [
{
columnName: 'companyId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'activityId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'personId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,60 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addActivityTable: WorkspaceMigrationTableAction[] = [
{
name: 'activity',
action: 'create',
},
{
name: 'activity',
action: 'alter',
columns: [
{
columnName: 'title',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'body',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'{}'",
},
{
columnName: 'type',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'Note'",
},
{
columnName: 'reminderAt',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'dueAt',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'completedAt',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'authorId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'assigneeId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,33 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addApiKeyTable: WorkspaceMigrationTableAction[] = [
{
name: 'apiKey',
action: 'create',
},
{
name: 'apiKey',
action: 'alter',
columns: [
{
columnName: 'name',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'revokedAt',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'expiresAt',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,55 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addAttachmentTable: WorkspaceMigrationTableAction[] = [
{
name: 'attachment',
action: 'create',
},
{
name: 'attachment',
action: 'alter',
columns: [
{
columnName: 'name',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'fullPath',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'type',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'companyId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'authorId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'activityId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'personId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,33 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addCommentTable: WorkspaceMigrationTableAction[] = [
{
name: 'comment',
action: 'create',
},
{
name: 'comment',
action: 'alter',
columns: [
{
columnName: 'body',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'authorId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'activityId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,38 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addFavoriteTable: WorkspaceMigrationTableAction[] = [
{
name: 'favorite',
action: 'create',
},
{
name: 'favorite',
action: 'alter',
columns: [
{
columnName: 'position',
columnType: 'float',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: 0,
},
{
columnName: 'companyId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'personId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'workspaceMemberId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,58 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addOpportunityTable: WorkspaceMigrationTableAction[] = [
{
name: 'opportunity',
action: 'create',
},
{
name: 'opportunity',
action: 'alter',
columns: [
{
columnName: 'amountAmountMicros',
columnType: 'numeric',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'amountCurrencyCode',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'probability',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'0'",
},
{
columnName: 'closeDate',
columnType: 'timestamp',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'companyId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'personId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'pipelineStepId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'pointOfContactId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,88 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addPersonTable: WorkspaceMigrationTableAction[] = [
{
name: 'person',
action: 'create',
},
{
name: 'person',
action: 'alter',
columns: [
{
columnName: 'nameFirstName',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'nameLastName',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'email',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'linkedinLinkUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'linkedinLinkLabel',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'xLinkUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'xLinkLabel',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'jobTitle',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'phone',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'city',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'avatarUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'companyId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,35 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addPipelineStepTable: WorkspaceMigrationTableAction[] = [
{
name: 'pipelineStep',
action: 'create',
},
{
name: 'pipelineStep',
action: 'alter',
columns: [
{
columnName: 'name',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'color',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'position',
columnType: 'float',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: 0,
},
],
},
];

View File

@ -0,0 +1,29 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addWebhookTable: WorkspaceMigrationTableAction[] = [
{
name: 'webhook',
action: 'create',
},
{
name: 'webhook',
action: 'alter',
columns: [
{
columnName: 'targetUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'operation',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
],
},
];

View File

@ -0,0 +1,51 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addWorkspaceMemberTable: WorkspaceMigrationTableAction[] = [
{
name: 'workspaceMember',
action: 'create',
},
{
name: 'workspaceMember',
action: 'alter',
columns: [
{
columnName: 'nameFirstName',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'nameLastName',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'avatarUrl',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
},
{
columnName: 'colorScheme',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'Light'",
},
{
columnName: 'locale',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "'en'",
},
{
columnName: 'userId',
columnType: 'uuid',
action: WorkspaceMigrationColumnActionType.CREATE,
},
],
},
];

View File

@ -0,0 +1,19 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addCompanyRelations: WorkspaceMigrationTableAction[] = [
{
name: 'company',
action: 'alter',
columns: [
{
columnName: 'accountOwnerId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,37 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addAttachmentRelations: WorkspaceMigrationTableAction[] = [
{
name: 'attachment',
action: 'alter',
columns: [
{
columnName: 'companyId',
referencedTableName: 'company',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'personId',
referencedTableName: 'person',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'activityId',
referencedTableName: 'activity',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'authorId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,19 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addPersonRelations: WorkspaceMigrationTableAction[] = [
{
name: 'person',
action: 'alter',
columns: [
{
columnName: 'companyId',
referencedTableName: 'company',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,31 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addFavoriteRelations: WorkspaceMigrationTableAction[] = [
{
name: 'favorite',
action: 'alter',
columns: [
{
columnName: 'companyId',
referencedTableName: 'company',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'personId',
referencedTableName: 'person',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'workspaceMemberId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,37 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addOpportunityRelations: WorkspaceMigrationTableAction[] = [
{
name: 'opportunity',
action: 'alter',
columns: [
{
columnName: 'companyId',
referencedTableName: 'company',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'personId',
referencedTableName: 'person',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'pointOfContactId',
referencedTableName: 'person',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'pipelineStepId',
referencedTableName: 'pipelineStep',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,31 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addActivityTargetRelations: WorkspaceMigrationTableAction[] = [
{
name: 'activityTarget',
action: 'alter',
columns: [
{
columnName: 'companyId',
referencedTableName: 'company',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'personId',
referencedTableName: 'person',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'activityId',
referencedTableName: 'activity',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,25 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addActivityRelations: WorkspaceMigrationTableAction[] = [
{
name: 'activity',
action: 'alter',
columns: [
{
columnName: 'authorId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'assigneeId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,25 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addCommentRelations: WorkspaceMigrationTableAction[] = [
{
name: 'comment',
action: 'alter',
columns: [
{
columnName: 'authorId',
referencedTableName: 'workspaceMember',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
{
columnName: 'activityId',
referencedTableName: 'activity',
referencedTableColumnName: 'id',
action: WorkspaceMigrationColumnActionType.RELATION,
},
],
},
];

View File

@ -0,0 +1,47 @@
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
export const addConnectedAccount: WorkspaceMigrationTableAction[] = [
{
name: 'connectedAccount',
action: 'create',
},
{
name: 'connectedAccount',
action: 'alter',
columns: [
{
columnName: 'type',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'accessToken',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'refreshToken',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'externalScopes',
columnType: 'varchar',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: "''",
},
{
columnName: 'hasEmailScope',
columnType: 'boolean',
action: WorkspaceMigrationColumnActionType.CREATE,
defaultValue: 'false',
},
],
},
];

View File

@ -0,0 +1,56 @@
import { addActivityTargetTable } from 'src/metadata/workspace-migration/migrations/1697618015-addActivityTargetTable';
import { addActivityTable } from 'src/metadata/workspace-migration/migrations/1697618016-addActivityTable';
import { addApiKeyTable } from 'src/metadata/workspace-migration/migrations/1697618017-addApiKeyTable';
import { addAttachmentTable } from 'src/metadata/workspace-migration/migrations/1697618018-addAttachmentTable';
import { addCommentTable } from 'src/metadata/workspace-migration/migrations/1697618019-addCommentTable';
import { addFavoriteTable } from 'src/metadata/workspace-migration/migrations/1697618020-addFavoriteTable';
import { addOpportunityTable } from 'src/metadata/workspace-migration/migrations/1697618021-addOpportunityTable';
import { addPersonTable } from 'src/metadata/workspace-migration/migrations/1697618022-addPersonTable';
import { addPipelineStepTable } from 'src/metadata/workspace-migration/migrations/1697618023-addPipelineStepTable';
import { addWebhookTable } from 'src/metadata/workspace-migration/migrations/1697618024-addWebhookTable';
import { addWorkspaceMemberTable } from 'src/metadata/workspace-migration/migrations/1697618026-addWorspaceMemberTable';
import { addCompanyRelations } from 'src/metadata/workspace-migration/migrations/1697618027-addCompanyRelations';
import { addAttachmentRelations } from 'src/metadata/workspace-migration/migrations/1697618028-addAttachmentRelations';
import { addPersonRelations } from 'src/metadata/workspace-migration/migrations/1697618029-addPersonRelations';
import { addFavoriteRelations } from 'src/metadata/workspace-migration/migrations/1697618030-addFavoriteRelations';
import { addActivityTargetRelations } from 'src/metadata/workspace-migration/migrations/1697618032-addActivityTargetRelations';
import { addActivityRelations } from 'src/metadata/workspace-migration/migrations/1697618033-addActivityRelations';
import { addCommentRelations } from 'src/metadata/workspace-migration/migrations/1697618034-addCommentRelations';
import { addOpportunityRelations } from 'src/metadata/workspace-migration/migrations/1697618031-addOpportunityRelations';
import { addCompanyTable } from './migrations/1697618009-addCompanyTable';
import { addViewTable } from './migrations/1697618011-addViewTable';
import { addViewFieldTable } from './migrations/1697618012-addViewFieldTable';
import { addViewFilterTable } from './migrations/1697618013-addViewFilterTable';
import { addViewSortTable } from './migrations/1697618014-addViewSortTable';
import { addConnectedAccount } from 'src/metadata/workspace-migration/migrations/1697618035-addConnectedAccount';
// TODO: read the folder and return all migrations
export const standardMigrations = {
'1697618009-addCompanyTable': addCompanyTable,
'1697618011-addViewTable': addViewTable,
'1697618012-addViewFieldTable': addViewFieldTable,
'1697618013-addViewFilterTable': addViewFilterTable,
'1697618014-addViewSortTable': addViewSortTable,
'1697618015-addActivityTargetTable': addActivityTargetTable,
'1697618016-addActivityTable': addActivityTable,
'1697618017-addApiKeyTable': addApiKeyTable,
'1697618018-addAttachmentTable': addAttachmentTable,
'1697618019-addCommentTable': addCommentTable,
'1697618020-addFavoriteTable': addFavoriteTable,
'1697618021-addOpportunityTable': addOpportunityTable,
'1697618022-addPersonTable': addPersonTable,
'1697618023-addPipelineStepTable': addPipelineStepTable,
'1697618024-addWebhookTable': addWebhookTable,
'1697618026-addWorkspaceMemberTable': addWorkspaceMemberTable,
'1697618027-addCompanyRelations': addCompanyRelations,
'1697618028-addAttachmentRelations': addAttachmentRelations,
'1697618029-addPersonRelations': addPersonRelations,
'1697618030-addFavoriteRelations': addFavoriteRelations,
'1697618031-addOpportunitiesRelations': addOpportunityRelations,
'1697618032-addActivityTargetRelations': addActivityTargetRelations,
'1697618033-addActivityRelations': addActivityRelations,
'1697618034-addCommentRelations': addCommentRelations,
'1697618035-addConnectedAccount': addConnectedAccount,
};

View File

@ -0,0 +1,34 @@
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export const fieldMetadataTypeToColumnType = <Type extends FieldMetadataType>(
fieldMetadataType: Type,
): string => {
/**
* Composite types are not implemented here, as they are flattened by their composite definitions.
* See src/metadata/field-metadata/composite-types for more information.
*/
switch (fieldMetadataType) {
case FieldMetadataType.UUID:
return 'uuid';
case FieldMetadataType.TEXT:
return 'text';
case FieldMetadataType.PHONE:
case FieldMetadataType.EMAIL:
return 'varchar';
case FieldMetadataType.NUMERIC:
return 'numeric';
case FieldMetadataType.NUMBER:
case FieldMetadataType.PROBABILITY:
return 'float';
case FieldMetadataType.BOOLEAN:
return 'boolean';
case FieldMetadataType.DATE_TIME:
return 'timestamp';
case FieldMetadataType.RATING:
case FieldMetadataType.SELECT:
case FieldMetadataType.MULTI_SELECT:
return 'enum';
default:
throw new Error(`Cannot convert ${fieldMetadataType} to column type.`);
}
};

View File

@ -0,0 +1,80 @@
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
export enum WorkspaceMigrationColumnActionType {
CREATE = 'CREATE',
ALTER = 'ALTER',
RELATION = 'RELATION',
}
export type WorkspaceMigrationEnum = string | { from: string; to: string };
export interface WorkspaceMigrationColumnDefinition {
columnName: string;
columnType: string;
enum?: WorkspaceMigrationEnum[];
isArray?: boolean;
isNullable?: boolean;
defaultValue?: any;
}
export interface WorkspaceMigrationColumnCreate
extends WorkspaceMigrationColumnDefinition {
action: WorkspaceMigrationColumnActionType.CREATE;
}
export type WorkspaceMigrationColumnAlter = {
action: WorkspaceMigrationColumnActionType.ALTER;
currentColumnDefinition: WorkspaceMigrationColumnDefinition;
alteredColumnDefinition: WorkspaceMigrationColumnDefinition;
};
export type WorkspaceMigrationColumnRelation = {
action: WorkspaceMigrationColumnActionType.RELATION;
columnName: string;
referencedTableName: string;
referencedTableColumnName: string;
isUnique?: boolean;
};
export type WorkspaceMigrationColumnAction = {
action: WorkspaceMigrationColumnActionType;
} & (
| WorkspaceMigrationColumnCreate
| WorkspaceMigrationColumnAlter
| WorkspaceMigrationColumnRelation
);
export type WorkspaceMigrationTableAction = {
name: string;
action: 'create' | 'alter';
columns?: WorkspaceMigrationColumnAction[];
};
@Entity('workspaceMigration')
export class WorkspaceMigrationEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true, type: 'jsonb' })
migrations: WorkspaceMigrationTableAction[];
@Column({ nullable: true })
name: string;
@Column({ default: false })
isCustom: boolean;
@Column({ nullable: true })
appliedAt?: Date;
@Column()
workspaceId: string;
@CreateDateColumn()
createdAt: Date;
}

View File

@ -0,0 +1,203 @@
import { Injectable, Logger } from '@nestjs/common';
import { WorkspaceColumnActionFactory } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-factory.interface';
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { BasicColumnActionFactory } from 'src/metadata/workspace-migration/factories/basic-column-action.factory';
import { EnumColumnActionFactory } from 'src/metadata/workspace-migration/factories/enum-column-action.factory';
import {
WorkspaceMigrationColumnAction,
WorkspaceMigrationColumnActionType,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
import { fullNameFields } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
import { currencyFields } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
import { linkFields } from 'src/metadata/field-metadata/composite-types/link.composite-type';
type CompositeFieldsDefinitionFunction = (
fieldMetadata: FieldMetadataInterface,
) => FieldMetadataInterface[];
@Injectable()
export class WorkspaceMigrationFactory {
private readonly logger = new Logger(WorkspaceMigrationFactory.name);
private factoriesMap: Map<
FieldMetadataType,
{
factory: WorkspaceColumnActionFactory<any>;
options?: WorkspaceColumnActionOptions;
}
>;
private compositeDefinitions = new Map<
string,
CompositeFieldsDefinitionFunction
>();
constructor(
private readonly basicColumnActionFactory: BasicColumnActionFactory,
private readonly enumColumnActionFactory: EnumColumnActionFactory,
) {
this.factoriesMap = new Map<
FieldMetadataType,
{
factory: WorkspaceColumnActionFactory<any>;
options?: WorkspaceColumnActionOptions;
}
>([
[FieldMetadataType.UUID, { factory: this.basicColumnActionFactory }],
[
FieldMetadataType.TEXT,
{
factory: this.basicColumnActionFactory,
options: {
defaultValue: '',
},
},
],
[
FieldMetadataType.PHONE,
{
factory: this.basicColumnActionFactory,
options: {
defaultValue: '',
},
},
],
[
FieldMetadataType.EMAIL,
{
factory: this.basicColumnActionFactory,
options: {
defaultValue: '',
},
},
],
[FieldMetadataType.NUMERIC, { factory: this.basicColumnActionFactory }],
[FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }],
[
FieldMetadataType.PROBABILITY,
{ factory: this.basicColumnActionFactory },
],
[FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }],
[FieldMetadataType.DATE_TIME, { factory: this.basicColumnActionFactory }],
[FieldMetadataType.RATING, { factory: this.enumColumnActionFactory }],
[FieldMetadataType.SELECT, { factory: this.enumColumnActionFactory }],
[
FieldMetadataType.MULTI_SELECT,
{ factory: this.enumColumnActionFactory },
],
]);
this.compositeDefinitions = new Map<string, CompositeFieldsDefinitionFunction>(
[
[FieldMetadataType.LINK, linkFields],
[FieldMetadataType.CURRENCY, currencyFields],
[FieldMetadataType.FULL_NAME, fullNameFields],
],
);
}
createColumnActions(
action: WorkspaceMigrationColumnActionType.CREATE,
fieldMetadata: FieldMetadataInterface,
): WorkspaceMigrationColumnAction[];
createColumnActions(
action: WorkspaceMigrationColumnActionType.ALTER,
currentFieldMetadata: FieldMetadataInterface,
alteredFieldMetadata: FieldMetadataInterface,
): WorkspaceMigrationColumnAction[];
createColumnActions(
action:
| WorkspaceMigrationColumnActionType.CREATE
| WorkspaceMigrationColumnActionType.ALTER,
fieldMetadataOrCurrentFieldMetadata: FieldMetadataInterface,
undefinedOrAlteredFieldMetadata?: FieldMetadataInterface,
): WorkspaceMigrationColumnAction[] {
const currentFieldMetadata =
action === WorkspaceMigrationColumnActionType.ALTER
? fieldMetadataOrCurrentFieldMetadata
: undefined;
const alteredFieldMetadata =
action === WorkspaceMigrationColumnActionType.CREATE
? fieldMetadataOrCurrentFieldMetadata
: undefinedOrAlteredFieldMetadata;
if (!alteredFieldMetadata) {
this.logger.error(
`No field metadata provided for action ${action}`,
undefinedOrAlteredFieldMetadata,
);
throw new Error(`No field metadata provided for action ${action}`);
}
// If it's a composite field type, we need to create a column action for each of the fields
if (isCompositeFieldMetadataType(alteredFieldMetadata.type)) {
const fieldMetadataSplitterFunction = this.compositeDefinitions.get(
alteredFieldMetadata.type,
);
if (!fieldMetadataSplitterFunction) {
this.logger.error(
`No composite definition found for type ${alteredFieldMetadata.type}`,
{
alteredFieldMetadata,
},
);
throw new Error(
`No composite definition found for type ${alteredFieldMetadata.type}`,
);
}
const fieldMetadataCollection =
fieldMetadataSplitterFunction(alteredFieldMetadata);
return fieldMetadataCollection.map((fieldMetadata) =>
this.createColumnAction(action, fieldMetadata, fieldMetadata),
);
}
// Otherwise, we create a single column action
const columnAction = this.createColumnAction(
action,
currentFieldMetadata,
alteredFieldMetadata,
);
return [columnAction];
}
private createColumnAction(
action:
| WorkspaceMigrationColumnActionType.CREATE
| WorkspaceMigrationColumnActionType.ALTER,
currentFieldMetadata: FieldMetadataInterface | undefined,
alteredFieldMetadata: FieldMetadataInterface,
): WorkspaceMigrationColumnAction {
const { factory, options } =
this.factoriesMap.get(alteredFieldMetadata.type) ?? {};
if (!factory) {
this.logger.error(
`No factory found for type ${alteredFieldMetadata.type}`,
{
alteredFieldMetadata,
},
);
throw new Error(`No factory found for type ${alteredFieldMetadata.type}`);
}
return factory.create(
action,
currentFieldMetadata,
alteredFieldMetadata,
options,
);
}
}

View File

@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { workspaceColumnActionFactories } from 'src/metadata/workspace-migration/factories/factories';
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
import { WorkspaceMigrationService } from './workspace-migration.service';
import { WorkspaceMigrationEntity } from './workspace-migration.entity';
@Module({
imports: [TypeOrmModule.forFeature([WorkspaceMigrationEntity], 'metadata')],
providers: [
...workspaceColumnActionFactories,
WorkspaceMigrationFactory,
WorkspaceMigrationService,
],
exports: [WorkspaceMigrationFactory, WorkspaceMigrationService],
})
export class WorkspaceMigrationModule {}

View File

@ -0,0 +1,111 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { IsNull, Repository } from 'typeorm';
import { standardMigrations } from './standard-migrations';
import {
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
} from './workspace-migration.entity';
@Injectable()
export class WorkspaceMigrationService {
constructor(
@InjectRepository(WorkspaceMigrationEntity, 'metadata')
private readonly workspaceMigrationRepository: Repository<WorkspaceMigrationEntity>,
) {}
/**
* Insert all standard migrations that have not been inserted yet
*
* @param workspaceId
*/
public async insertStandardMigrations(workspaceId: string): Promise<void> {
// TODO: we actually don't need to fetch all of them, to improve later so it scales well.
const insertedStandardMigrations =
await this.workspaceMigrationRepository.find({
where: { workspaceId, isCustom: false },
});
const insertedStandardMigrationsMapByName =
insertedStandardMigrations.reduce((acc, migration) => {
acc[migration.name] = migration;
return acc;
}, {});
const standardMigrationsListThatNeedToBeInserted = Object.entries(
standardMigrations,
)
.filter(([name]) => !insertedStandardMigrationsMapByName[name])
.map(([name, migrations]) => ({ name, migrations }));
const standardMigrationsThatNeedToBeInserted =
standardMigrationsListThatNeedToBeInserted.map((migration) => ({
...migration,
workspaceId,
isCustom: false,
}));
await this.workspaceMigrationRepository.save(
standardMigrationsThatNeedToBeInserted,
);
}
/**
* Get all pending migrations for a given workspaceId
*
* @param workspaceId: string
* @returns Promise<WorkspaceMigration[]>
*/
public async getPendingMigrations(
workspaceId: string,
): Promise<WorkspaceMigrationEntity[]> {
return await this.workspaceMigrationRepository.find({
order: { createdAt: 'ASC', name: 'ASC' },
where: {
appliedAt: IsNull(),
workspaceId,
},
});
}
/**
* Set appliedAt as current date for a given migration.
* Should be called once the migration has been applied
*
* @param workspaceId: string
* @param migration: WorkspaceMigration
*/
public async setAppliedAtForMigration(
workspaceId: string,
migration: WorkspaceMigrationEntity,
) {
await this.workspaceMigrationRepository.save({
id: migration.id,
appliedAt: new Date(),
});
}
/**
* Create a new pending migration for a given workspaceId and expected changes
*
* @param workspaceId
* @param migrations
*/
public async createCustomMigration(
workspaceId: string,
migrations: WorkspaceMigrationTableAction[],
) {
await this.workspaceMigrationRepository.save({
migrations,
workspaceId,
isCustom: true,
});
}
public async delete(workspaceId: string) {
await this.workspaceMigrationRepository.delete({ workspaceId });
}
}