feat: refactor folder structure (#4498)
* feat: wip refactor folder structure * Fix * fix position --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,153 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory';
|
||||
import { computeStandardObject } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-object.util';
|
||||
import { StandardFieldFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory';
|
||||
import { CustomObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata';
|
||||
|
||||
@Command({
|
||||
name: 'workspace:add-standard-id',
|
||||
description: 'Add standard id to all metadata objects and fields',
|
||||
})
|
||||
export class AddStandardIdCommand extends CommandRunner {
|
||||
private readonly logger = new Logger(AddStandardIdCommand.name);
|
||||
|
||||
constructor(
|
||||
@InjectDataSource('metadata')
|
||||
private readonly metadataDataSource: DataSource,
|
||||
private readonly standardObjectFactory: StandardObjectFactory,
|
||||
private readonly standardFieldFactory: StandardFieldFactory,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const queryRunner = this.metadataDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
const manager = queryRunner.manager;
|
||||
|
||||
this.logger.log('Adding standardId to metadata objects and fields');
|
||||
|
||||
try {
|
||||
const standardObjectMetadataCollection =
|
||||
this.standardObjectFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
{
|
||||
// We don't need to provide the workspace id and data source id as we're only adding standardId
|
||||
workspaceId: '',
|
||||
dataSourceId: '',
|
||||
},
|
||||
{
|
||||
IS_BLOCKLIST_ENABLED: true,
|
||||
IS_CALENDAR_ENABLED: true,
|
||||
},
|
||||
);
|
||||
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
||||
CustomObjectMetadata,
|
||||
{
|
||||
workspaceId: '',
|
||||
dataSourceId: '',
|
||||
},
|
||||
{
|
||||
IS_BLOCKLIST_ENABLED: true,
|
||||
IS_CALENDAR_ENABLED: true,
|
||||
},
|
||||
);
|
||||
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
const fieldMetadataRepository =
|
||||
manager.getRepository(FieldMetadataEntity);
|
||||
|
||||
/**
|
||||
* Update all object metadata with standard id
|
||||
*/
|
||||
const updateObjectMetadataCollection: Partial<ObjectMetadataEntity>[] =
|
||||
[];
|
||||
const updateFieldMetadataCollection: Partial<FieldMetadataEntity>[] = [];
|
||||
const originalObjectMetadataCollection =
|
||||
await objectMetadataRepository.find({
|
||||
where: {
|
||||
fields: { isCustom: false },
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
const customObjectMetadataCollection =
|
||||
originalObjectMetadataCollection.filter(
|
||||
(metadata) => metadata.isCustom,
|
||||
);
|
||||
const standardObjectMetadataMap = new Map(
|
||||
standardObjectMetadataCollection.map((metadata) => [
|
||||
metadata.nameSingular,
|
||||
metadata,
|
||||
]),
|
||||
);
|
||||
|
||||
for (const originalObjectMetadata of originalObjectMetadataCollection) {
|
||||
const standardObjectMetadata = standardObjectMetadataMap.get(
|
||||
originalObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
if (!standardObjectMetadata && !originalObjectMetadata.isCustom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const computedStandardObjectMetadata = computeStandardObject(
|
||||
standardObjectMetadata ?? {
|
||||
...originalObjectMetadata,
|
||||
fields: standardFieldMetadataCollection,
|
||||
},
|
||||
originalObjectMetadata,
|
||||
customObjectMetadataCollection,
|
||||
);
|
||||
|
||||
if (
|
||||
!originalObjectMetadata.isCustom &&
|
||||
!originalObjectMetadata.standardId
|
||||
) {
|
||||
updateObjectMetadataCollection.push({
|
||||
id: originalObjectMetadata.id,
|
||||
standardId: computedStandardObjectMetadata.standardId,
|
||||
});
|
||||
}
|
||||
|
||||
for (const fieldMetadata of originalObjectMetadata.fields) {
|
||||
const standardFieldMetadata =
|
||||
computedStandardObjectMetadata.fields.find(
|
||||
(field) => field.name === fieldMetadata.name && !field.isCustom,
|
||||
);
|
||||
|
||||
if (!standardFieldMetadata || fieldMetadata.standardId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
updateFieldMetadataCollection.push({
|
||||
id: fieldMetadata.id,
|
||||
standardId: standardFieldMetadata.standardId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await objectMetadataRepository.save(updateObjectMetadataCollection);
|
||||
|
||||
await fieldMetadataRepository.save(updateFieldMetadataCollection);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error('Error adding standard id to metadata', error);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine-metadata/workspace-migration/workspace-migration.entity';
|
||||
import { CommandLogger } from 'src/commands/command-logger';
|
||||
|
||||
@Injectable()
|
||||
export class SyncWorkspaceLoggerService {
|
||||
private readonly commandLogger = new CommandLogger(
|
||||
SyncWorkspaceLoggerService.name,
|
||||
);
|
||||
|
||||
constructor() {}
|
||||
|
||||
async saveLogs(
|
||||
workspaceId: string,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceMigrations: WorkspaceMigrationEntity[],
|
||||
) {
|
||||
// Create sub directory
|
||||
await this.commandLogger.createSubDirectory(workspaceId);
|
||||
|
||||
// Save workspace migrations
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/workspace-migrations`,
|
||||
workspaceMigrations,
|
||||
);
|
||||
|
||||
// Save object metadata create collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/object-metadata-create-collection`,
|
||||
storage.objectMetadataCreateCollection,
|
||||
);
|
||||
|
||||
// Save object metadata update collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/object-metadata-update-collection`,
|
||||
storage.objectMetadataUpdateCollection,
|
||||
);
|
||||
|
||||
// Save object metadata delete collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/object-metadata-delete-collection`,
|
||||
storage.objectMetadataDeleteCollection,
|
||||
);
|
||||
|
||||
// Save field metadata create collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/field-metadata-create-collection`,
|
||||
storage.fieldMetadataCreateCollection,
|
||||
);
|
||||
|
||||
// Save field metadata update collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/field-metadata-update-collection`,
|
||||
storage.fieldMetadataUpdateCollection,
|
||||
);
|
||||
|
||||
// Save field metadata delete collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/field-metadata-delete-collection`,
|
||||
storage.fieldMetadataDeleteCollection,
|
||||
);
|
||||
|
||||
// Save relation metadata create collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/relation-metadata-create-collection`,
|
||||
storage.relationMetadataCreateCollection,
|
||||
);
|
||||
|
||||
// Save relation metadata update collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/relation-metadata-update-collection`,
|
||||
storage.relationMetadataUpdateCollection,
|
||||
);
|
||||
|
||||
// Save relation metadata delete collection
|
||||
await this.commandLogger.writeLog(
|
||||
`${workspaceId}/relation-metadata-delete-collection`,
|
||||
storage.relationMetadataDeleteCollection,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { DataSourceService } from 'src/engine-metadata/data-source/data-source.service';
|
||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
||||
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
||||
import { WorkspaceService } from 'src/engine/modules/workspace/services/workspace.service';
|
||||
|
||||
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
||||
|
||||
// TODO: implement dry-run
|
||||
interface RunWorkspaceMigrationsOptions {
|
||||
workspaceId?: string;
|
||||
dryRun?: boolean;
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: 'workspace:sync-metadata',
|
||||
description: 'Sync metadata',
|
||||
})
|
||||
export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
||||
private readonly logger = new Logger(SyncWorkspaceMetadataCommand.name);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
|
||||
private readonly workspaceHealthService: WorkspaceHealthService,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(
|
||||
_passedParam: string[],
|
||||
options: RunWorkspaceMigrationsOptions,
|
||||
): Promise<void> {
|
||||
const workspaceIds = options.workspaceId
|
||||
? [options.workspaceId]
|
||||
: await this.workspaceService.getWorkspaceIds();
|
||||
|
||||
for (const workspaceId of workspaceIds) {
|
||||
const issues = await this.workspaceHealthService.healthCheck(workspaceId);
|
||||
|
||||
// Security: abort if there are issues.
|
||||
if (issues.length > 0) {
|
||||
if (!options.force) {
|
||||
this.logger.error(
|
||||
`Workspace contains ${issues.length} issues, aborting.`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
'If you want to force the migration, use --force flag',
|
||||
);
|
||||
this.logger.log(
|
||||
'Please use `workspace:health` command to check issues and fix them before running this command.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warn(
|
||||
`Workspace contains ${issues.length} issues, sync has been forced.`,
|
||||
);
|
||||
}
|
||||
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const { storage, workspaceMigrations } =
|
||||
await this.workspaceSyncMetadataService.synchronize(
|
||||
{
|
||||
workspaceId,
|
||||
dataSourceId: dataSourceMetadata.id,
|
||||
},
|
||||
{ applyChanges: !options.dryRun },
|
||||
);
|
||||
|
||||
if (options.dryRun) {
|
||||
await this.syncWorkspaceLoggerService.saveLogs(
|
||||
workspaceId,
|
||||
storage,
|
||||
workspaceMigrations,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-w, --workspace-id [workspace_id]',
|
||||
description: 'workspace id',
|
||||
required: false,
|
||||
})
|
||||
parseWorkspaceId(value: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-d, --dry-run',
|
||||
description: 'Dry run without applying changes',
|
||||
required: false,
|
||||
})
|
||||
dryRun(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-f, --force',
|
||||
description: 'Force migration',
|
||||
required: false,
|
||||
})
|
||||
force(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
|
||||
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
||||
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
||||
import { WorkspaceModule } from 'src/engine/modules/workspace/workspace.module';
|
||||
import { AddStandardIdCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command';
|
||||
|
||||
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
|
||||
|
||||
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceSyncMetadataModule,
|
||||
WorkspaceHealthModule,
|
||||
WorkspaceModule,
|
||||
DataSourceModule,
|
||||
],
|
||||
providers: [
|
||||
SyncWorkspaceMetadataCommand,
|
||||
AddStandardIdCommand,
|
||||
SyncWorkspaceLoggerService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceSyncMetadataCommandsModule {}
|
||||
Reference in New Issue
Block a user