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:
Jérémy M
2024-03-15 14:40:58 +01:00
committed by GitHub
parent 52f1b3ac98
commit 94487f6737
760 changed files with 3215 additions and 3155 deletions

View File

@ -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();
}
}
}

View File

@ -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,
);
}
}

View File

@ -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;
}
}

View File

@ -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 {}