Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set custom object is soft deletable command #6788

Merged
merged 8 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { InjectRepository } from '@nestjs/typeorm';

import { Command } from 'nest-commander';
import { In, Repository } from 'typeorm';

import { ActiveWorkspacesCommandRunner } from 'src/database/commands/upgrade-version/active-workspaces.command';
import { BaseCommandOptions } from 'src/database/commands/upgrade-version/base.command';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';

interface SetCustomObjectIsSoftDeletableCommandOptions
extends BaseCommandOptions {}

@Command({
name: 'upgrade-0-24:set-custom-object-is-soft-deletable',
description: 'Set custom object is soft deletable',
})
export class SetCustomObjectIsSoftDeletableCommand extends ActiveWorkspacesCommandRunner {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it!

constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
) {
super(workspaceRepository);
}

async executeActiveWorkspacesCommand(
_passedParam: string[],
options: SetCustomObjectIsSoftDeletableCommandOptions,
workspaceIds: string[],
): Promise<void> {
const updateCriteria = {
workspaceId: In(workspaceIds),
isCustom: true,
isSoftDeletable: false,
};

if (options.dryRun) {
const entitiesToUpdate = await this.objectMetadataRepository.find({
select: ['id'],
where: updateCriteria,
});

this.logger.log(
`Dry run: ${entitiesToUpdate.length} entities would be updated`,
);

return;
}

const result = await this.objectMetadataRepository.update(updateCriteria, {
isSoftDeletable: true,
});

this.logger.log(`Updated ${result.affected} entities`);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { SetCustomObjectIsSoftDeletableCommand } from 'src/database/commands/upgrade-version/0-24/0-24-set-custom-object-is-soft-deletable.command';
import { SetMessageDirectionCommand } from 'src/database/commands/upgrade-version/0-24/0-24-set-message-direction.command';
import { UpgradeTo0_24Command } from 'src/database/commands/upgrade-version/0-24/0-24-upgrade-version.command';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
Expand Down Expand Up @@ -33,6 +34,10 @@ import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manage
),
TypeORMModule,
],
providers: [UpgradeTo0_24Command, SetMessageDirectionCommand],
providers: [
UpgradeTo0_24Command,
SetMessageDirectionCommand,
SetCustomObjectIsSoftDeletableCommand,
],
})
export class UpgradeTo0_24CommandModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';

import chalk from 'chalk';
import { Option } from 'nest-commander';
import { Repository } from 'typeorm';

import {
BaseCommandOptions,
BaseCommandRunner,
} from 'src/database/commands/upgrade-version/base.command';
import {
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';

export type ActiveWorkspacesCommandOptions = BaseCommandOptions & {
workspaceId?: string;
};

export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
private workspaceIds: string[] = [];

protected readonly logger: Logger;

constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
) {
super();
this.logger = new Logger(this.constructor.name);
}

@Option({
flags: '-w, --workspace-id [workspace_id]',
description:
'workspace id. Command runs on all active workspaces if not provided',
required: false,
})
parseWorkspaceId(val: string): string[] {
this.workspaceIds.push(val);

return this.workspaceIds;
}

protected async fetchActiveWorkspaceIds(): Promise<string[]> {
const activeWorkspaces = await this.workspaceRepository.find({
select: ['id'],
where: {
activationStatus: WorkspaceActivationStatus.ACTIVE,
},
});

return activeWorkspaces.map((workspace) => workspace.id);
}

protected logWorkspaceCount(activeWorkspaceIds: string[]): void {
if (!activeWorkspaceIds.length) {
this.logger.log(chalk.yellow('No workspace found'));
} else {
this.logger.log(
chalk.green(
`Running command on ${activeWorkspaceIds.length} workspaces`,
),
);
}
}

override async executeBaseCommand(
passedParams: string[],
options: BaseCommandOptions,
): Promise<void> {
const activeWorkspaceIds =
this.workspaceIds.length > 0
? this.workspaceIds
: await this.fetchActiveWorkspaceIds();

this.logWorkspaceCount(activeWorkspaceIds);

if (options.dryRun) {
this.logger.log(chalk.yellow('Dry run mode: No changes will be applied'));
}

await this.executeActiveWorkspacesCommand(
passedParams,
options,
activeWorkspaceIds,
);
}

protected abstract executeActiveWorkspacesCommand(
passedParams: string[],
options: BaseCommandOptions,
activeWorkspaceIds: string[],
): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Logger } from '@nestjs/common';

import chalk from 'chalk';
import { CommandRunner, Option } from 'nest-commander';

export type BaseCommandOptions = {
workspaceId?: string;
dryRun?: boolean;
};

export abstract class BaseCommandRunner extends CommandRunner {
protected readonly logger: Logger;

constructor() {
super();
this.logger = new Logger(this.constructor.name);
}

@Option({
flags: '-d, --dry-run',
description: 'Simulate the command without making actual changes',
required: false,
})
parseDryRun(): boolean {
return true;
}
Weiko marked this conversation as resolved.
Show resolved Hide resolved

override async run(
passedParams: string[],
options: BaseCommandOptions,
): Promise<void> {
try {
await this.executeBaseCommand(passedParams, options);
} catch (error) {
this.logger.log(chalk.red(`Command failed`));
Weiko marked this conversation as resolved.
Show resolved Hide resolved
throw error;
} finally {
this.logger.log(chalk.blue('Command completed!'));
}
}

protected abstract executeBaseCommand(
passedParams: string[],
options: BaseCommandOptions,
): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TypeOrmModuleOptions } from '@nestjs/typeorm';

import { DataSource, DataSourceOptions } from 'typeorm';
import { config } from 'dotenv';
import { DataSource, DataSourceOptions } from 'typeorm';
config();

export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export class GraphqlQuerySelectedFieldsRelationParser {
fieldValue: any,
result: { select: Record<string, any>; relations: Record<string, any> },
): void {
result.relations[fieldKey] = true;

if (!fieldValue || typeof fieldValue !== 'object') {
return;
}

result.relations[fieldKey] = true;

const referencedObjectMetadata = getRelationObjectMetadata(
fieldMetadata,
this.objectMetadataMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ export class GraphqlQueryRunnerService {
const objectMetadata = objectMetadataMap[objectMetadataItem.nameSingular];

if (!objectMetadata) {
throw new Error(
`Object metadata for ${objectMetadataItem.nameSingular} not found`,
throw new GraphqlQueryRunnerException(
`Object metadata not found for ${objectMetadataItem.nameSingular}`,
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}

Expand Down
Loading