feat: migration can be applied on a specific schema & some enhancements (#2998)
* fix: remove old metadata seed files * feat: wip standard to core relation * fix: lint * fix: merge * fix: remove debug files * feat: add feature flag for core object metadata * fix: remove debug * feat: always disable the standard core relation * fix: missing feature flag * fix: remove debug * fix: feature flag doesn't seems to disable relation * fix: delete .vscode folder, change this in another PR * Update packages/twenty-server/src/workspace/workspace-sync-metadata/reflective-metadata.factory.ts Co-authored-by: Weiko <corentin@twenty.com> * Update packages/twenty-server/src/workspace/workspace-sync-metadata/reflective-metadata.factory.ts Co-authored-by: Weiko <corentin@twenty.com> * Update packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync.metadata.service.ts Co-authored-by: Weiko <corentin@twenty.com> * fix: remove optional fields from metadata entities * fix: renamed variable * fix: put back CursorScalarType * fix: delete test command * fix: remove unused workspace standard migration command * fix: drop core object metadata declaration * fix: rename variable * fix: drop creation of core datasource * fix: remove feature flag * fix: drop support of standard to core relations * feat: add user email field on workspace-member standard object * fix: update seed accordingly * fix: missing remove command file * fix: datasource label should remain nullable * fix: better asserts * Remove unused code * Remove unused code --------- Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,45 +0,0 @@
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
|
||||
|
||||
// TODO: implement dry-run
|
||||
interface RunWorkspaceMigrationsOptions {
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: 'workspace:migrate',
|
||||
description: 'Run workspace migrations',
|
||||
})
|
||||
export class RunWorkspaceMigrationsCommand extends CommandRunner {
|
||||
constructor(
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(
|
||||
_passedParam: string[],
|
||||
options: RunWorkspaceMigrationsOptions,
|
||||
): Promise<void> {
|
||||
// TODO: run in a dedicated job + run queries in a transaction.
|
||||
await this.workspaceMigrationService.insertStandardMigrations(
|
||||
options.workspaceId,
|
||||
);
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
options.workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: workspaceId should be optional and we should run migrations for all workspaces
|
||||
@Option({
|
||||
flags: '-w, --workspace-id [workspace_id]',
|
||||
description: 'workspace id',
|
||||
required: true,
|
||||
})
|
||||
parseWorkspaceId(value: string): string {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.module';
|
||||
|
||||
import { RunWorkspaceMigrationsCommand } from './run-workspace-migrations.command';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceMigrationModule, WorkspaceMigrationRunnerModule],
|
||||
providers: [RunWorkspaceMigrationsCommand],
|
||||
})
|
||||
export class WorkspaceMigrationRunnerCommandsModule {}
|
||||
@ -63,13 +63,11 @@ export class WorkspaceMigrationRunnerService {
|
||||
}, []);
|
||||
|
||||
const queryRunner = workspaceDataSource?.createQueryRunner();
|
||||
const schemaName =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
// Loop over each migration and create or update the table
|
||||
// TODO: Should be done in a transaction
|
||||
for (const migration of flattenedPendingMigrations) {
|
||||
await this.handleTableChanges(queryRunner, schemaName, migration);
|
||||
await this.handleTableChanges(queryRunner, migration);
|
||||
}
|
||||
|
||||
// Update appliedAt date for each migration
|
||||
@ -98,17 +96,20 @@ export class WorkspaceMigrationRunnerService {
|
||||
*/
|
||||
private async handleTableChanges(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableMigration: WorkspaceMigrationTableAction,
|
||||
) {
|
||||
switch (tableMigration.action) {
|
||||
case 'create':
|
||||
await this.createTable(queryRunner, schemaName, tableMigration.name);
|
||||
await this.createTable(
|
||||
queryRunner,
|
||||
tableMigration.schemaName,
|
||||
tableMigration.name,
|
||||
);
|
||||
break;
|
||||
case 'alter':
|
||||
await this.handleColumnChanges(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableMigration.schemaName,
|
||||
tableMigration.name,
|
||||
tableMigration?.columns,
|
||||
);
|
||||
@ -180,7 +181,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.RELATION:
|
||||
await this.createForeignKey(
|
||||
await this.createRelation(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
@ -279,10 +280,9 @@ export class WorkspaceMigrationRunnerService {
|
||||
isNullable: migrationColumn.alteredColumnDefinition.isNullable,
|
||||
}),
|
||||
);
|
||||
// }
|
||||
}
|
||||
|
||||
private async createForeignKey(
|
||||
private async createRelation(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
@ -293,6 +293,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
new TableForeignKey({
|
||||
columnNames: [migrationColumn.columnName],
|
||||
referencedColumnNames: [migrationColumn.referencedTableColumnName],
|
||||
referencedSchema: migrationColumn.referencedSchema,
|
||||
referencedTableName: migrationColumn.referencedTableName,
|
||||
onDelete: 'CASCADE',
|
||||
}),
|
||||
|
||||
@ -104,12 +104,12 @@ export class RelationFieldAliasFactory {
|
||||
);
|
||||
|
||||
return `
|
||||
${fieldKey}: ${referencedObjectMetadata.targetTableName}Collection${
|
||||
${fieldKey}: ${referencedObjectMetadata.targetTableName}Collection${
|
||||
argsString ? `(${argsString})` : ''
|
||||
} {
|
||||
${fieldsString}
|
||||
}
|
||||
`;
|
||||
${fieldsString}
|
||||
}
|
||||
`;
|
||||
}
|
||||
let relationAlias = fieldMetadata.isCustom
|
||||
? `${fieldKey}: ${fieldMetadata.targetColumnMap.value}`
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { CursorScalarType } from './cursor.scalar';
|
||||
import { BigFloatScalarType } from './big-float.scalar';
|
||||
import { BigIntScalarType } from './big-int.scalar';
|
||||
import { CursorScalarType } from './cursor.scalar';
|
||||
import { DateScalarType } from './date.scalar';
|
||||
import { DateTimeScalarType } from './date-time.scalar';
|
||||
import { TimeScalarType } from './time.scalar';
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
import {
|
||||
FieldMetadataDecoratorParams,
|
||||
ReflectFieldMetadata,
|
||||
} from 'src/workspace/workspace-sync-metadata/interfaces/reflect-field-metadata.interface';
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.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 { generateDefaultValue } from 'src/metadata/field-metadata/utils/generate-default-value';
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
|
||||
export function FieldMetadata<T extends FieldMetadataType>(
|
||||
params: FieldMetadataDecoratorParams<T>,
|
||||
): PropertyDecorator {
|
||||
return (target: object, fieldKey: string) => {
|
||||
const existingFieldMetadata =
|
||||
TypedReflect.getMetadata('fieldMetadata', target.constructor) ?? {};
|
||||
const isNullable =
|
||||
TypedReflect.getMetadata('isNullable', target, fieldKey) ?? false;
|
||||
const isSystem =
|
||||
TypedReflect.getMetadata('isSystem', target, fieldKey) ?? false;
|
||||
const gate = TypedReflect.getMetadata('gate', target, fieldKey);
|
||||
const { joinColumn, ...restParams } = params;
|
||||
|
||||
TypedReflect.defineMetadata(
|
||||
'fieldMetadata',
|
||||
{
|
||||
...existingFieldMetadata,
|
||||
[fieldKey]: generateFieldMetadata<T>(
|
||||
restParams,
|
||||
fieldKey,
|
||||
isNullable,
|
||||
isSystem,
|
||||
gate,
|
||||
),
|
||||
...(joinColumn && restParams.type === FieldMetadataType.RELATION
|
||||
? {
|
||||
[joinColumn]: generateFieldMetadata<FieldMetadataType.UUID>(
|
||||
{
|
||||
...restParams,
|
||||
type: FieldMetadataType.UUID,
|
||||
label: `${restParams.label} id (foreign key)`,
|
||||
description: `${restParams.description} id foreign key`,
|
||||
defaultValue: null,
|
||||
},
|
||||
joinColumn,
|
||||
isNullable,
|
||||
true,
|
||||
gate,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
target.constructor,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function generateFieldMetadata<T extends FieldMetadataType>(
|
||||
params: FieldMetadataDecoratorParams<T>,
|
||||
fieldKey: string,
|
||||
isNullable: boolean,
|
||||
isSystem: boolean,
|
||||
gate: GateDecoratorParams | undefined = undefined,
|
||||
): ReflectFieldMetadata[string] {
|
||||
const targetColumnMap = generateTargetColumnMap(params.type, false, fieldKey);
|
||||
const defaultValue = params.defaultValue ?? generateDefaultValue(params.type);
|
||||
|
||||
return {
|
||||
name: fieldKey,
|
||||
...params,
|
||||
targetColumnMap: JSON.stringify(targetColumnMap),
|
||||
isNullable: params.type === FieldMetadataType.RELATION ? true : isNullable,
|
||||
isSystem,
|
||||
isCustom: false,
|
||||
// TODO: handle options + stringify for the diff.
|
||||
description: params.description,
|
||||
icon: params.icon,
|
||||
defaultValue: defaultValue ? JSON.stringify(defaultValue) : null,
|
||||
gate,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.interface';
|
||||
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
|
||||
export function Gate(metadata: GateDecoratorParams) {
|
||||
return function (target: object, fieldKey?: string) {
|
||||
if (fieldKey) {
|
||||
TypedReflect.defineMetadata('gate', metadata, target, fieldKey);
|
||||
} else {
|
||||
TypedReflect.defineMetadata('gate', metadata, target);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
|
||||
export function IsNullable() {
|
||||
return function (target: object, fieldKey: string) {
|
||||
TypedReflect.defineMetadata('isNullable', true, target, fieldKey);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
|
||||
export function IsSystem() {
|
||||
return function (target: object, fieldKey?: string) {
|
||||
if (fieldKey) {
|
||||
TypedReflect.defineMetadata('isSystem', true, target, fieldKey);
|
||||
} else {
|
||||
TypedReflect.defineMetadata('isSystem', true, target);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,210 +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';
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import { generateDefaultValue } from 'src/metadata/field-metadata/utils/generate-default-value';
|
||||
|
||||
export interface FieldMetadataDecorator<T extends FieldMetadataType> {
|
||||
type: T;
|
||||
label: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
defaultValue?: FieldMetadataDefaultValue<T> | 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;
|
||||
};
|
||||
|
||||
export type GateDecorator = {
|
||||
featureFlag: 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 gate = Reflect.getMetadata('gate', target) || undefined;
|
||||
|
||||
const objectName = convertClassNameToObjectMetadataName(target.name);
|
||||
|
||||
Reflect.defineMetadata(
|
||||
'objectMetadata',
|
||||
{
|
||||
nameSingular: objectName,
|
||||
...metadata,
|
||||
gate,
|
||||
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 Gate(metadata: GateDecorator) {
|
||||
return function (target: object, fieldKey?: string) {
|
||||
if (fieldKey) {
|
||||
Reflect.defineMetadata('gate', metadata, target, fieldKey);
|
||||
} else {
|
||||
Reflect.defineMetadata('gate', metadata, target);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function FieldMetadata<T extends FieldMetadataType>(
|
||||
metadata: FieldMetadataDecorator<T>,
|
||||
): 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 gate = Reflect.getMetadata('gate', target, fieldKey) || undefined;
|
||||
|
||||
const { joinColumn, ...fieldMetadata } = metadata;
|
||||
|
||||
Reflect.defineMetadata(
|
||||
'fieldMetadata',
|
||||
{
|
||||
...existingFieldMetadata,
|
||||
[fieldKey]: generateFieldMetadata<T>(
|
||||
fieldMetadata,
|
||||
fieldKey,
|
||||
isNullable,
|
||||
isSystem,
|
||||
gate,
|
||||
),
|
||||
...(joinColumn && fieldMetadata.type === FieldMetadataType.RELATION
|
||||
? {
|
||||
[joinColumn]: generateFieldMetadata<FieldMetadataType.UUID>(
|
||||
{
|
||||
...fieldMetadata,
|
||||
type: FieldMetadataType.UUID,
|
||||
label: `${fieldMetadata.label} id (foreign key)`,
|
||||
description: `${fieldMetadata.description} id foreign key`,
|
||||
defaultValue: null,
|
||||
},
|
||||
joinColumn,
|
||||
isNullable,
|
||||
true,
|
||||
gate,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
target.constructor,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function generateFieldMetadata<T extends FieldMetadataType>(
|
||||
metadata: FieldMetadataDecorator<T>,
|
||||
fieldKey: string,
|
||||
isNullable: boolean,
|
||||
isSystem: boolean,
|
||||
gate: GateDecorator | undefined = undefined,
|
||||
) {
|
||||
const targetColumnMap = JSON.stringify(
|
||||
generateTargetColumnMap(metadata.type, false, fieldKey),
|
||||
);
|
||||
const defaultValue =
|
||||
metadata.defaultValue ?? generateDefaultValue(metadata.type);
|
||||
|
||||
return {
|
||||
name: fieldKey,
|
||||
...metadata,
|
||||
targetColumnMap: targetColumnMap,
|
||||
isNullable:
|
||||
metadata.type === FieldMetadataType.RELATION ? true : 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,
|
||||
gate,
|
||||
};
|
||||
}
|
||||
|
||||
export function RelationMetadata(
|
||||
metadata: RelationMetadataDecorator,
|
||||
): PropertyDecorator {
|
||||
return (target: object, fieldKey: string) => {
|
||||
const existingRelationMetadata =
|
||||
Reflect.getMetadata('relationMetadata', target.constructor) || [];
|
||||
|
||||
const gate = Reflect.getMetadata('gate', target, fieldKey) || undefined;
|
||||
|
||||
const objectName = convertClassNameToObjectMetadataName(
|
||||
target.constructor.name,
|
||||
);
|
||||
|
||||
Reflect.defineMetadata(
|
||||
'relationMetadata',
|
||||
[
|
||||
...existingRelationMetadata,
|
||||
{
|
||||
type: metadata.type,
|
||||
fromObjectNameSingular: objectName,
|
||||
toObjectNameSingular: metadata.objectName,
|
||||
fromFieldMetadataName: fieldKey,
|
||||
toFieldMetadataName: metadata.inverseSideFieldName ?? objectName,
|
||||
gate,
|
||||
},
|
||||
],
|
||||
target.constructor,
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import { ObjectMetadataDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-object-metadata.interface';
|
||||
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
import { convertClassNameToObjectMetadataName } from 'src/workspace/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||
|
||||
export function ObjectMetadata(
|
||||
params: ObjectMetadataDecoratorParams,
|
||||
): ClassDecorator {
|
||||
return (target) => {
|
||||
const isSystem = TypedReflect.getMetadata('isSystem', target) ?? false;
|
||||
const gate = TypedReflect.getMetadata('gate', target);
|
||||
const objectName = convertClassNameToObjectMetadataName(target.name);
|
||||
|
||||
TypedReflect.defineMetadata(
|
||||
'objectMetadata',
|
||||
{
|
||||
nameSingular: objectName,
|
||||
...params,
|
||||
targetTableName: objectName,
|
||||
isSystem,
|
||||
isCustom: false,
|
||||
description: params.description,
|
||||
icon: params.icon,
|
||||
gate,
|
||||
},
|
||||
target,
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { RelationMetadataDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-relation-metadata.interface';
|
||||
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
import { convertClassNameToObjectMetadataName } from 'src/workspace/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||
|
||||
export function RelationMetadata(
|
||||
params: RelationMetadataDecoratorParams,
|
||||
): PropertyDecorator {
|
||||
return (target: object, fieldKey: string) => {
|
||||
const existingRelationMetadata =
|
||||
TypedReflect.getMetadata('relationMetadata', target.constructor) ?? [];
|
||||
const gate = TypedReflect.getMetadata('gate', target, fieldKey);
|
||||
const objectName = convertClassNameToObjectMetadataName(
|
||||
target.constructor.name,
|
||||
);
|
||||
|
||||
Reflect.defineMetadata(
|
||||
'relationMetadata',
|
||||
[
|
||||
...existingRelationMetadata,
|
||||
{
|
||||
type: params.type,
|
||||
fromObjectNameSingular: objectName,
|
||||
toObjectNameSingular: params.objectName,
|
||||
fromFieldMetadataName: fieldKey,
|
||||
toFieldMetadataName: params.inverseSideFieldName ?? objectName,
|
||||
gate,
|
||||
},
|
||||
],
|
||||
target.constructor,
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
export interface GateDecoratorParams {
|
||||
featureFlag: string;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { PartialFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||
import { PartialObjectMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export type MappedFieldMetadata = Record<string, PartialFieldMetadata>;
|
||||
|
||||
export interface MappedObjectMetadata
|
||||
extends Omit<PartialObjectMetadata, 'fields'> {
|
||||
fields: MappedFieldMetadata;
|
||||
}
|
||||
|
||||
export type MappedFieldMetadataEntity = Record<string, FieldMetadataEntity>;
|
||||
|
||||
export interface MappedObjectMetadataEntity
|
||||
extends Omit<ObjectMetadataEntity, 'fields'> {
|
||||
fields: MappedFieldMetadataEntity;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
import { ReflectFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-field-metadata.interface';
|
||||
|
||||
export type PartialFieldMetadata = ReflectFieldMetadata[string] & {
|
||||
workspaceId: string;
|
||||
objectMetadataId?: string;
|
||||
};
|
||||
@ -0,0 +1,8 @@
|
||||
import { PartialFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||
import { ReflectObjectMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-object-metadata.interface';
|
||||
|
||||
export type PartialObjectMetadata = ReflectObjectMetadata & {
|
||||
workspaceId: string;
|
||||
dataSourceId: string;
|
||||
fields: PartialFieldMetadata[];
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export interface FieldMetadataDecoratorParams<
|
||||
T extends FieldMetadataType | 'default',
|
||||
> {
|
||||
type: T;
|
||||
label: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||
joinColumn?: string;
|
||||
}
|
||||
|
||||
export interface ReflectFieldMetadata {
|
||||
[key: string]: Omit<
|
||||
FieldMetadataDecoratorParams<'default'>,
|
||||
'defaultValue' | 'type'
|
||||
> & {
|
||||
name: string;
|
||||
type: FieldMetadataType;
|
||||
targetColumnMap: string;
|
||||
isNullable: boolean;
|
||||
isSystem: boolean;
|
||||
isCustom: boolean;
|
||||
description?: string;
|
||||
defaultValue: string | null;
|
||||
gate?: GateDecoratorParams;
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.interface';
|
||||
|
||||
export interface ObjectMetadataDecoratorParams {
|
||||
namePlural: string;
|
||||
labelSingular: string;
|
||||
labelPlural: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface ReflectObjectMetadata extends ObjectMetadataDecoratorParams {
|
||||
nameSingular: string;
|
||||
targetTableName: string;
|
||||
isSystem: boolean;
|
||||
isCustom: boolean;
|
||||
gate?: GateDecoratorParams;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
export interface RelationMetadataDecoratorParams {
|
||||
type: RelationMetadataType;
|
||||
objectName: string;
|
||||
inverseSideFieldName?: string;
|
||||
}
|
||||
|
||||
export interface ReflectRelationMetadata {
|
||||
type: RelationMetadataType;
|
||||
fromObjectNameSingular: string;
|
||||
toObjectNameSingular: string;
|
||||
fromFieldMetadataName: string;
|
||||
toFieldMetadataName: string;
|
||||
gate?: GateDecoratorParams;
|
||||
}
|
||||
@ -1,18 +1,25 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
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';
|
||||
import { PartialObjectMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
||||
import { MappedObjectMetadataEntity } from 'src/workspace/workspace-sync-metadata/interfaces/mapped-metadata.interface';
|
||||
|
||||
export class MetadataParser {
|
||||
static parseMetadata(
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
import { isGatedAndNotEnabled } from 'src/workspace/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
|
||||
|
||||
@Injectable()
|
||||
export class ReflectiveMetadataFactory {
|
||||
async createObjectMetadata(
|
||||
metadata: typeof BaseObjectMetadata,
|
||||
workspaceId: string,
|
||||
dataSourceId: string,
|
||||
defaultDataSourceId: string,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
): ObjectMetadataEntity | undefined {
|
||||
const objectMetadata = Reflect.getMetadata('objectMetadata', metadata);
|
||||
const fieldMetadata = Reflect.getMetadata('fieldMetadata', metadata);
|
||||
): Promise<PartialObjectMetadata | undefined> {
|
||||
const objectMetadata = TypedReflect.getMetadata('objectMetadata', metadata);
|
||||
const fieldMetadata =
|
||||
TypedReflect.getMetadata('fieldMetadata', metadata) ?? {};
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new Error(
|
||||
@ -31,47 +38,48 @@ export class MetadataParser {
|
||||
return {
|
||||
...objectMetadata,
|
||||
workspaceId,
|
||||
dataSourceId,
|
||||
fields: fields.map((field: FieldMetadataEntity) => ({
|
||||
dataSourceId: defaultDataSourceId,
|
||||
fields: fields.map((field) => ({
|
||||
...field,
|
||||
workspaceId,
|
||||
isSystem: objectMetadata.isSystem || field.isSystem,
|
||||
defaultValue: field.defaultValue || null,
|
||||
options: field.options || null,
|
||||
defaultValue: field.defaultValue,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static parseAllMetadata(
|
||||
async createObjectMetadataCollection(
|
||||
metadataCollection: (typeof BaseObjectMetadata)[],
|
||||
workspaceId: string,
|
||||
dataSourceId: string,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
): ObjectMetadataEntity[] {
|
||||
return metadataCollection
|
||||
.map((metadata) =>
|
||||
MetadataParser.parseMetadata(
|
||||
metadata,
|
||||
workspaceId,
|
||||
dataSourceId,
|
||||
workspaceFeatureFlagsMap,
|
||||
),
|
||||
)
|
||||
.filter(
|
||||
(metadata): metadata is ObjectMetadataEntity => metadata !== undefined,
|
||||
);
|
||||
) {
|
||||
const metadataPromises = metadataCollection.map((metadata) =>
|
||||
this.createObjectMetadata(
|
||||
metadata,
|
||||
workspaceId,
|
||||
dataSourceId,
|
||||
workspaceFeatureFlagsMap,
|
||||
),
|
||||
);
|
||||
const resolvedMetadata = await Promise.all(metadataPromises);
|
||||
|
||||
return resolvedMetadata.filter(
|
||||
(metadata): metadata is PartialObjectMetadata => !!metadata,
|
||||
);
|
||||
}
|
||||
|
||||
static parseRelationMetadata(
|
||||
createRelationMetadata(
|
||||
metadata: typeof BaseObjectMetadata,
|
||||
workspaceId: string,
|
||||
objectMetadataFromDB: Record<string, ObjectMetadataEntity>,
|
||||
objectMetadataFromDB: Record<string, MappedObjectMetadataEntity>,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
) {
|
||||
const objectMetadata = Reflect.getMetadata('objectMetadata', metadata);
|
||||
const relationMetadata = Reflect.getMetadata('relationMetadata', metadata);
|
||||
|
||||
if (!relationMetadata) return [];
|
||||
const objectMetadata = TypedReflect.getMetadata('objectMetadata', metadata);
|
||||
const relationMetadata = TypedReflect.getMetadata(
|
||||
'relationMetadata',
|
||||
metadata,
|
||||
);
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new Error(
|
||||
@ -79,7 +87,10 @@ export class MetadataParser {
|
||||
);
|
||||
}
|
||||
|
||||
if (isGatedAndNotEnabled(objectMetadata, workspaceFeatureFlagsMap)) {
|
||||
if (
|
||||
!relationMetadata ||
|
||||
isGatedAndNotEnabled(objectMetadata, workspaceFeatureFlagsMap)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -94,7 +105,7 @@ export class MetadataParser {
|
||||
assert(
|
||||
fromObjectMetadata,
|
||||
`Object ${relation.fromObjectNameSingular} not found in DB
|
||||
for fromRelation defined in class ${objectMetadata.nameSingular}`,
|
||||
for relation FROM defined in class ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
|
||||
const toObjectMetadata =
|
||||
@ -103,7 +114,7 @@ export class MetadataParser {
|
||||
assert(
|
||||
toObjectMetadata,
|
||||
`Object ${relation.toObjectNameSingular} not found in DB
|
||||
for toRelation defined in class ${objectMetadata.nameSingular}`,
|
||||
for relation TO defined in class ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
|
||||
const fromFieldMetadata =
|
||||
@ -112,7 +123,7 @@ export class MetadataParser {
|
||||
assert(
|
||||
fromFieldMetadata,
|
||||
`Field ${relation.fromFieldMetadataName} not found in object ${relation.fromObjectNameSingular}
|
||||
for fromRelation defined in class ${objectMetadata.nameSingular}`,
|
||||
for relation FROM defined in class ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
|
||||
const toFieldMetadata =
|
||||
@ -121,7 +132,7 @@ export class MetadataParser {
|
||||
assert(
|
||||
toFieldMetadata,
|
||||
`Field ${relation.toFieldMetadataName} not found in object ${relation.toObjectNameSingular}
|
||||
for toRelation defined in class ${objectMetadata.nameSingular}`,
|
||||
for relation TO defined in class ${objectMetadata.nameSingular}`,
|
||||
);
|
||||
|
||||
return {
|
||||
@ -135,14 +146,14 @@ export class MetadataParser {
|
||||
});
|
||||
}
|
||||
|
||||
static parseAllRelations(
|
||||
createRelationMetadataCollection(
|
||||
metadataCollection: (typeof BaseObjectMetadata)[],
|
||||
workspaceId: string,
|
||||
objectMetadataFromDB: Record<string, ObjectMetadataEntity>,
|
||||
objectMetadataFromDB: Record<string, MappedObjectMetadataEntity>,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
) {
|
||||
return metadataCollection.flatMap((metadata) =>
|
||||
MetadataParser.parseRelationMetadata(
|
||||
this.createRelationMetadata(
|
||||
metadata,
|
||||
workspaceId,
|
||||
objectMetadataFromDB,
|
||||
@ -151,14 +162,3 @@ export class MetadataParser {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function isGatedAndNotEnabled(
|
||||
metadata,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
): boolean {
|
||||
const featureFlagValue =
|
||||
metadata.gate?.featureFlag &&
|
||||
workspaceFeatureFlagsMap[metadata.gate.featureFlag];
|
||||
|
||||
return metadata.gate?.featureFlag !== undefined && !featureFlagValue;
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { ActivityTargetObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity-target.object-metadata';
|
||||
import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
FieldMetadata,
|
||||
IsNullable,
|
||||
IsSystem,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
|
||||
@ObjectMetadata({
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
FieldMetadata,
|
||||
IsSystem,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
|
||||
export abstract class BaseObjectMetadata {
|
||||
@FieldMetadata({
|
||||
type: FieldMetadataType.UUID,
|
||||
label: 'Id',
|
||||
icon: null,
|
||||
description: null,
|
||||
defaultValue: { type: 'uuid' },
|
||||
})
|
||||
@IsSystem()
|
||||
@ -18,7 +14,6 @@ export abstract class BaseObjectMetadata {
|
||||
@FieldMetadata({
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
label: 'Creation date',
|
||||
description: null,
|
||||
icon: 'IconCalendar',
|
||||
defaultValue: { type: 'now' },
|
||||
})
|
||||
@ -27,7 +22,6 @@ export abstract class BaseObjectMetadata {
|
||||
@FieldMetadata({
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
label: 'Update date',
|
||||
description: null,
|
||||
icon: 'IconCalendar',
|
||||
defaultValue: { type: 'now' },
|
||||
})
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
IsSystem,
|
||||
FieldMetadata,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
|
||||
|
||||
@ -2,12 +2,10 @@ import { CurrencyMetadata } from 'src/metadata/field-metadata/composite-types/cu
|
||||
import { LinkMetadata } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { ActivityTargetObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity-target.object-metadata';
|
||||
import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
||||
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||
import { PersonObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata';
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
||||
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
IsSystem,
|
||||
FieldMetadata,
|
||||
IsNullable,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata';
|
||||
import { PersonObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata';
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
||||
import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata';
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { MessageRecipientObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata';
|
||||
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { CurrencyMetadata } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
FieldMetadata,
|
||||
IsNullable,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||
import { PersonObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata';
|
||||
|
||||
@ -2,14 +2,12 @@ import { FullNameMetadata } from 'src/metadata/field-metadata/composite-types/fu
|
||||
import { LinkMetadata } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { ActivityTargetObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity-target.object-metadata';
|
||||
import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { OpportunityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/opportunity.object-metadata';
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { ViewObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata';
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
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 { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { ViewObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata';
|
||||
|
||||
@ -21,7 +19,6 @@ export class ViewFilterObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.UUID,
|
||||
label: 'Field Metadata Id',
|
||||
description: 'View Filter target field',
|
||||
icon: null,
|
||||
})
|
||||
fieldMetadataId: string;
|
||||
|
||||
@ -29,7 +26,6 @@ export class ViewFilterObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Operand',
|
||||
description: 'View Filter operand',
|
||||
icon: null,
|
||||
defaultValue: { value: 'Contains' },
|
||||
})
|
||||
operand: string;
|
||||
@ -38,7 +34,6 @@ export class ViewFilterObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Value',
|
||||
description: 'View Filter value',
|
||||
icon: null,
|
||||
defaultValue: { value: '' },
|
||||
})
|
||||
value: string;
|
||||
@ -47,7 +42,6 @@ export class ViewFilterObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Display Value',
|
||||
description: 'View Filter Display Value',
|
||||
icon: null,
|
||||
defaultValue: { value: '' },
|
||||
})
|
||||
displayValue: string;
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
FieldMetadata,
|
||||
IsNullable,
|
||||
IsSystem,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
import { ViewObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata';
|
||||
|
||||
@ -29,7 +27,6 @@ export class ViewSortObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Direction',
|
||||
description: 'View Sort direction',
|
||||
icon: null,
|
||||
defaultValue: { value: 'asc' },
|
||||
})
|
||||
direction: string;
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
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,
|
||||
IsNullable,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.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';
|
||||
@ -25,7 +23,6 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Name',
|
||||
description: 'View name',
|
||||
icon: null,
|
||||
defaultValue: { value: '' },
|
||||
})
|
||||
name: string;
|
||||
@ -34,7 +31,6 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.UUID,
|
||||
label: 'Object Metadata Id',
|
||||
description: 'View target object',
|
||||
icon: null,
|
||||
})
|
||||
objectMetadataId: string;
|
||||
|
||||
@ -42,7 +38,6 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Type',
|
||||
description: 'View type',
|
||||
icon: null,
|
||||
defaultValue: { value: 'table' },
|
||||
})
|
||||
type: string;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
ObjectMetadata,
|
||||
IsSystem,
|
||||
FieldMetadata,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
|
||||
@ObjectMetadata({
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { FullNameMetadata } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
|
||||
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,
|
||||
Gate,
|
||||
} from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator';
|
||||
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||
import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata';
|
||||
import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata';
|
||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||
@ -61,6 +59,14 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
|
||||
})
|
||||
avatarUrl: string;
|
||||
|
||||
@FieldMetadata({
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'User Email',
|
||||
description: 'Related user email address',
|
||||
icon: 'IconMail',
|
||||
})
|
||||
userEmail: string;
|
||||
|
||||
@FieldMetadata({
|
||||
type: FieldMetadataType.UUID,
|
||||
label: 'User Id',
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
export const convertClassNameToObjectMetadataName = (name: string): string => {
|
||||
const classSuffix = 'ObjectMetadata';
|
||||
let objectName = camelCase(name);
|
||||
|
||||
if (objectName.endsWith(classSuffix)) {
|
||||
objectName = objectName.slice(0, -classSuffix.length);
|
||||
}
|
||||
|
||||
return objectName;
|
||||
};
|
||||
@ -0,0 +1,10 @@
|
||||
export const isGatedAndNotEnabled = (
|
||||
metadata,
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
): boolean => {
|
||||
const featureFlagValue =
|
||||
metadata.gate?.featureFlag &&
|
||||
workspaceFeatureFlagsMap[metadata.gate.featureFlag];
|
||||
|
||||
return metadata.gate?.featureFlag !== undefined && !featureFlagValue;
|
||||
};
|
||||
@ -1,4 +1,6 @@
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||
import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
||||
|
||||
/**
|
||||
* This utility function filters out properties from an object based on a list of properties to ignore.
|
||||
@ -28,9 +30,12 @@ export const filterIgnoredProperties = (
|
||||
* @param arr - The array of ObjectMetadataEntity objects to convert.
|
||||
* @returns A map of object metadata, with nameSingular as the key and the object as the value.
|
||||
*/
|
||||
export const mapObjectMetadataByUniqueIdentifier = (
|
||||
arr: ObjectMetadataEntity[],
|
||||
) => {
|
||||
export const mapObjectMetadataByUniqueIdentifier = <
|
||||
T extends { nameSingular: string; fields: U[] },
|
||||
U extends { name: string },
|
||||
>(
|
||||
arr: T[],
|
||||
): Record<string, Omit<T, 'fields'> & { fields: Record<string, U> }> => {
|
||||
return arr.reduce((acc, curr) => {
|
||||
acc[curr.nameSingular] = {
|
||||
...curr,
|
||||
@ -38,29 +43,41 @@ export const mapObjectMetadataByUniqueIdentifier = (
|
||||
acc[curr.name] = curr;
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
}, {} as Record<string, U>),
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}, {} as Record<string, Omit<T, 'fields'> & { fields: Record<string, U> }>);
|
||||
};
|
||||
|
||||
export const convertStringifiedFieldsToJSON = (fieldMetadata) => {
|
||||
export const convertStringifiedFieldsToJSON = <
|
||||
T extends {
|
||||
targetColumnMap?: string | null;
|
||||
defaultValue?: string | null;
|
||||
options?: string | null;
|
||||
},
|
||||
>(
|
||||
fieldMetadata: T,
|
||||
): T & {
|
||||
targetColumnMap?: FieldMetadataTargetColumnMap;
|
||||
defaultValue?: FieldMetadataDefaultValue;
|
||||
options?: FieldMetadataOptions;
|
||||
} => {
|
||||
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,
|
||||
);
|
||||
fieldMetadata.targetColumnMap = JSON.parse(fieldMetadata.targetColumnMap);
|
||||
}
|
||||
|
||||
return fieldMetadata;
|
||||
if (fieldMetadata.defaultValue) {
|
||||
fieldMetadata.defaultValue = JSON.parse(fieldMetadata.defaultValue);
|
||||
}
|
||||
|
||||
if (fieldMetadata.options) {
|
||||
fieldMetadata.options = JSON.parse(fieldMetadata.options);
|
||||
}
|
||||
|
||||
return fieldMetadata as T & {
|
||||
targetColumnMap?: FieldMetadataTargetColumnMap;
|
||||
defaultValue?: FieldMetadataDefaultValue;
|
||||
options?: FieldMetadataOptions;
|
||||
};
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@ import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-
|
||||
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 { ReflectiveMetadataFactory } from 'src/workspace/workspace-sync-metadata/reflective-metadata.factory';
|
||||
import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metadata/workspace-sync.metadata.service';
|
||||
|
||||
@Module({
|
||||
@ -25,7 +26,7 @@ import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metad
|
||||
),
|
||||
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
|
||||
],
|
||||
providers: [WorkspaceSyncMetadataService, ReflectiveMetadataFactory],
|
||||
exports: [WorkspaceSyncMetadataService],
|
||||
providers: [WorkspaceSyncMetadataService],
|
||||
})
|
||||
export class WorkspaceSyncMetadataModule {}
|
||||
|
||||
@ -2,9 +2,16 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import diff from 'microdiff';
|
||||
import { Repository } from 'typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import camelCase from 'lodash.camelcase';
|
||||
|
||||
import { PartialFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||
import { PartialObjectMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
||||
import {
|
||||
MappedFieldMetadataEntity,
|
||||
MappedObjectMetadata,
|
||||
} from 'src/workspace/workspace-sync-metadata/interfaces/mapped-metadata.interface';
|
||||
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
@ -14,7 +21,6 @@ import {
|
||||
RelationMetadataEntity,
|
||||
RelationMetadataType,
|
||||
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import { MetadataParser } from 'src/workspace/workspace-sync-metadata/utils/metadata.parser';
|
||||
import {
|
||||
filterIgnoredProperties,
|
||||
convertStringifiedFieldsToJSON,
|
||||
@ -29,6 +35,7 @@ import {
|
||||
} 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';
|
||||
import { ReflectiveMetadataFactory } from 'src/workspace/workspace-sync-metadata/reflective-metadata.factory';
|
||||
import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity';
|
||||
|
||||
@Injectable()
|
||||
@ -36,6 +43,7 @@ export class WorkspaceSyncMetadataService {
|
||||
constructor(
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
private readonly reflectiveMetadataFactory: ReflectiveMetadataFactory,
|
||||
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@ -75,32 +83,37 @@ export class WorkspaceSyncMetadataService {
|
||||
{},
|
||||
);
|
||||
|
||||
const standardObjects = MetadataParser.parseAllMetadata(
|
||||
standardObjectMetadata,
|
||||
workspaceId,
|
||||
dataSourceId,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
const standardObjects =
|
||||
await this.reflectiveMetadataFactory.createObjectMetadataCollection(
|
||||
standardObjectMetadata,
|
||||
workspaceId,
|
||||
dataSourceId,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const objectsInDB = await this.objectMetadataRepository.find({
|
||||
where: { workspaceId, dataSourceId, isCustom: false },
|
||||
relations: ['fields'],
|
||||
where: { workspaceId, isCustom: false },
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
|
||||
const objectsInDBByName =
|
||||
mapObjectMetadataByUniqueIdentifier(objectsInDB);
|
||||
const standardObjectsByName =
|
||||
mapObjectMetadataByUniqueIdentifier(standardObjects);
|
||||
const objectsInDBByName = mapObjectMetadataByUniqueIdentifier<
|
||||
ObjectMetadataEntity,
|
||||
FieldMetadataEntity
|
||||
>(objectsInDB);
|
||||
const standardObjectsByName = mapObjectMetadataByUniqueIdentifier<
|
||||
PartialObjectMetadata,
|
||||
PartialFieldMetadata
|
||||
>(standardObjects);
|
||||
|
||||
const objectsToCreate: ObjectMetadataEntity[] = [];
|
||||
const objectsToCreate: MappedObjectMetadata[] = [];
|
||||
const objectsToDelete = objectsInDB.filter(
|
||||
(objectInDB) => !standardObjectsByName[objectInDB.nameSingular],
|
||||
);
|
||||
const objectsToUpdate: Record<string, ObjectMetadataEntity> = {};
|
||||
|
||||
const fieldsToCreate: FieldMetadataEntity[] = [];
|
||||
const fieldsToCreate: PartialFieldMetadata[] = [];
|
||||
const fieldsToDelete: FieldMetadataEntity[] = [];
|
||||
const fieldsToUpdate: Record<string, FieldMetadataEntity> = {};
|
||||
const fieldsToUpdate: Record<string, MappedFieldMetadataEntity> = {};
|
||||
|
||||
for (const standardObjectName in standardObjectsByName) {
|
||||
const standardObject = standardObjectsByName[standardObjectName];
|
||||
@ -187,13 +200,15 @@ export class WorkspaceSyncMetadataService {
|
||||
for (const diff of fieldsDiff) {
|
||||
const fieldName = diff.path[0];
|
||||
|
||||
if (diff.type === 'CREATE')
|
||||
if (diff.type === 'CREATE') {
|
||||
fieldsToCreate.push({
|
||||
...standardObjectFields[fieldName],
|
||||
objectMetadataId: objectInDB.id,
|
||||
});
|
||||
if (diff.type === 'REMOVE' && diff.path.length === 1)
|
||||
}
|
||||
if (diff.type === 'REMOVE' && diff.path.length === 1) {
|
||||
fieldsToDelete.push(objectInDBFields[fieldName]);
|
||||
}
|
||||
if (diff.type === 'CHANGE') {
|
||||
const property = diff.path[diff.path.length - 1];
|
||||
|
||||
@ -206,16 +221,25 @@ export class WorkspaceSyncMetadataService {
|
||||
}
|
||||
|
||||
// CREATE OBJECTS
|
||||
await this.objectMetadataRepository.save(
|
||||
objectsToCreate.map((object) => ({
|
||||
...object,
|
||||
isActive: true,
|
||||
fields: Object.values(object.fields).map((field) => ({
|
||||
...convertStringifiedFieldsToJSON(field),
|
||||
const createdObjectMetadataCollection =
|
||||
await this.objectMetadataRepository.save(
|
||||
objectsToCreate.map((object) => ({
|
||||
...object,
|
||||
isActive: true,
|
||||
fields: Object.values(object.fields).map((field) => ({
|
||||
...convertStringifiedFieldsToJSON(field),
|
||||
isActive: true,
|
||||
})),
|
||||
})),
|
||||
})),
|
||||
);
|
||||
const identifiers = createdObjectMetadataCollection.map(
|
||||
(object) => object.id,
|
||||
);
|
||||
const createdObjects = await this.objectMetadataRepository.find({
|
||||
where: { id: In(identifiers) },
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
|
||||
// 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);
|
||||
@ -228,9 +252,10 @@ export class WorkspaceSyncMetadataService {
|
||||
}
|
||||
|
||||
// CREATE FIELDS
|
||||
await this.fieldMetadataRepository.save(
|
||||
const createdFields = await this.fieldMetadataRepository.save(
|
||||
fieldsToCreate.map((field) => convertStringifiedFieldsToJSON(field)),
|
||||
);
|
||||
|
||||
// UPDATE FIELDS
|
||||
for (const [key, value] of Object.entries(fieldsToUpdate)) {
|
||||
await this.fieldMetadataRepository.update(
|
||||
@ -252,9 +277,9 @@ export class WorkspaceSyncMetadataService {
|
||||
|
||||
// Generate migrations
|
||||
await this.generateMigrationsFromSync(
|
||||
objectsToCreate,
|
||||
createdObjects,
|
||||
objectsToDelete,
|
||||
fieldsToCreate,
|
||||
createdFields,
|
||||
fieldsToDelete,
|
||||
objectsInDB,
|
||||
);
|
||||
@ -282,16 +307,20 @@ export class WorkspaceSyncMetadataService {
|
||||
workspaceFeatureFlagsMap: Record<string, boolean>,
|
||||
) {
|
||||
const objectsInDB = await this.objectMetadataRepository.find({
|
||||
where: { workspaceId, dataSourceId, isCustom: false },
|
||||
relations: ['fields'],
|
||||
where: { workspaceId, isCustom: false },
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
const objectsInDBByName = mapObjectMetadataByUniqueIdentifier(objectsInDB);
|
||||
const standardRelations = MetadataParser.parseAllRelations(
|
||||
standardObjectMetadata,
|
||||
workspaceId,
|
||||
objectsInDBByName,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
const objectsInDBByName = mapObjectMetadataByUniqueIdentifier<
|
||||
ObjectMetadataEntity,
|
||||
FieldMetadataEntity
|
||||
>(objectsInDB);
|
||||
const standardRelations =
|
||||
this.reflectiveMetadataFactory.createRelationMetadataCollection(
|
||||
standardObjectMetadata,
|
||||
workspaceId,
|
||||
objectsInDBByName,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
// TODO: filter out custom relations once isCustom has been added to relationMetadata table
|
||||
const relationsInDB = await this.relationMetadataRepository.find({
|
||||
@ -364,6 +393,7 @@ export class WorkspaceSyncMetadataService {
|
||||
{
|
||||
name: object.targetTableName,
|
||||
action: 'create',
|
||||
schemaName: object.dataSource.schema,
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
...Object.values(object.fields)
|
||||
.filter((field) => field.type !== FieldMetadataType.RELATION)
|
||||
@ -372,6 +402,7 @@ export class WorkspaceSyncMetadataService {
|
||||
({
|
||||
name: object.targetTableName,
|
||||
action: 'alter',
|
||||
schemaName: object.dataSource.schema,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
field,
|
||||
@ -395,7 +426,7 @@ export class WorkspaceSyncMetadataService {
|
||||
result[currentObject.id] = currentObject;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
}, {} as Record<string, ObjectMetadataEntity>);
|
||||
|
||||
if (fieldsToCreate.length > 0) {
|
||||
fieldsToCreate.map((field) => {
|
||||
@ -403,6 +434,8 @@ export class WorkspaceSyncMetadataService {
|
||||
{
|
||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
||||
action: 'alter',
|
||||
schemaName:
|
||||
objectsInDbById[field.objectMetadataId].dataSource.schema,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
field,
|
||||
@ -424,6 +457,8 @@ export class WorkspaceSyncMetadataService {
|
||||
{
|
||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
||||
action: 'alter',
|
||||
schemaName:
|
||||
objectsInDbById[field.objectMetadataId].dataSource.schema,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
@ -489,11 +524,13 @@ export class WorkspaceSyncMetadataService {
|
||||
{
|
||||
name: toObjectMetadata.targetTableName,
|
||||
action: 'alter',
|
||||
schemaName: toObjectMetadata.dataSource.schema,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||
referencedTableName: fromObjectMetadata.targetTableName,
|
||||
referencedSchema: fromObjectMetadata.dataSource.schema,
|
||||
referencedTableColumnName: 'id',
|
||||
isUnique:
|
||||
relation.relationType === RelationMetadataType.ONE_TO_ONE,
|
||||
|
||||
Reference in New Issue
Block a user