-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add set custom object is soft deletable command (#6788)
## Context Custom object were not automatically created as softDeletable, this has been fixed in a recent PR. This PR adds a command to backfill existing custom objects. We also introduce a baseCommandRunner and ActiveWorkspacesCommandRunner to put some boilerplate and simplify future commands. ## Test ```bash yarn command:prod upgrade-0-24:set-custom-object-is-soft-deletable [Nest] 75852 - 08/29/2024, 5:16:41 PM LOG [SetCustomObjectIsSoftDeletableCommand] Running command on 2 workspaces query: UPDATE "metadata"."objectMetadata" SET "isSoftDeletable" = $1, "updatedAt" = CURRENT_TIMESTAMP WHERE ("workspaceId" IN ($2, $3) AND "isCustom" = $4 AND "isSoftDeletable" = $5) -- PARAMETERS: [true,"3b8e6458-5fc1-4e63-8563-008ccddaa6db","20202020-1c25-4d02-bf25-6aeccf7ea419",true,false] [Nest] 75852 - 08/29/2024, 5:16:41 PM LOG [SetCustomObjectIsSoftDeletableCommand] Updated 1 entities [Nest] 75852 - 08/29/2024, 5:16:41 PM LOG [SetCustomObjectIsSoftDeletableCommand] Command completed! ``` ```bash yarn command:prod upgrade-0-24:set-custom-object-is-soft-deletable -d [Nest] 75424 - 08/29/2024, 5:16:14 PM LOG [SetCustomObjectIsSoftDeletableCommand] Running command on 2 workspaces [Nest] 75424 - 08/29/2024, 5:16:14 PM LOG [SetCustomObjectIsSoftDeletableCommand] Dry run mode: No changes will be applied query: SELECT "ObjectMetadataEntity"."id" AS "ObjectMetadataEntity_id" FROM "metadata"."objectMetadata" "ObjectMetadataEntity" WHERE (("ObjectMetadataEntity"."workspaceId" IN ($1, $2)) AND ("ObjectMetadataEntity"."isCustom" = $3) AND ("ObjectMetadataEntity"."isSoftDeletable" = $4)) -- PARAMETERS: ["3b8e6458-5fc1-4e63-8563-008ccddaa6db","20202020-1c25-4d02-bf25-6aeccf7ea419",true,false] [Nest] 75424 - 08/29/2024, 5:16:14 PM LOG [SetCustomObjectIsSoftDeletableCommand] Dry run: 1 entities would be updated [Nest] 75424 - 08/29/2024, 5:16:14 PM LOG [SetCustomObjectIsSoftDeletableCommand] Command completed! ``` ```bash yarn command:prod upgrade-0-24:set-custom-object-is-soft-deletable -w 20202020-1c25-4d02-bf25-6aeccf7ea419 -w 20202020-1c25-4d02-bf25-6aeccf7ea419 query: UPDATE "metadata"."objectMetadata" SET "isSoftDeletable" = $1, "updatedAt" = CURRENT_TIMESTAMP WHERE ("workspaceId" IN ($2, $3) AND "isCustom" = $4) -- PARAMETERS: [true,"20202020-1c25-4d02-bf25-6aeccf7ea419","20202020-1c25-4d02-bf25-6aeccf7ea419",true] [Nest] 70588 - 08/29/2024, 5:11:31 PM LOG [SetCustomObjectIsSoftDeletableCommand] Updated 2 entities [Nest] 70588 - 08/29/2024, 5:11:31 PM LOG [SetCustomObjectIsSoftDeletableCommand] Command completed! ``` --------- Co-authored-by: Charles Bochet <[email protected]>
- Loading branch information
1 parent
c5572f1
commit 7df5f91
Showing
8 changed files
with
216 additions
and
9 deletions.
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
packages/twenty-server/src/database/commands/active-workspaces.command.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { Logger } from '@nestjs/common'; | ||
|
||
import chalk from 'chalk'; | ||
import { Option } from 'nest-commander'; | ||
import { Repository } from 'typeorm'; | ||
|
||
import { | ||
BaseCommandOptions, | ||
BaseCommandRunner, | ||
} from 'src/database/commands/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(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>; | ||
} |
46 changes: 46 additions & 0 deletions
46
packages/twenty-server/src/database/commands/base.command.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
override async run( | ||
passedParams: string[], | ||
options: BaseCommandOptions, | ||
): Promise<void> { | ||
try { | ||
await this.executeBaseCommand(passedParams, options); | ||
} catch (error) { | ||
this.logger.error(chalk.red(`Command failed`)); | ||
throw error; | ||
} finally { | ||
this.logger.log(chalk.blue('Command completed!')); | ||
} | ||
} | ||
|
||
protected abstract executeBaseCommand( | ||
passedParams: string[], | ||
options: BaseCommandOptions, | ||
): Promise<void>; | ||
} |
60 changes: 60 additions & 0 deletions
60
...atabase/commands/upgrade-version/0-24/0-24-set-custom-object-is-soft-deletable.command.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
|
||
import { Command } from 'nest-commander'; | ||
import { In, Repository } from 'typeorm'; | ||
|
||
import { | ||
ActiveWorkspacesCommandOptions, | ||
ActiveWorkspacesCommandRunner, | ||
} from 'src/database/commands/active-workspaces.command'; | ||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; | ||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; | ||
|
||
type SetCustomObjectIsSoftDeletableCommandOptions = | ||
ActiveWorkspacesCommandOptions; | ||
|
||
@Command({ | ||
name: 'upgrade-0.24:set-custom-object-is-soft-deletable', | ||
description: 'Set custom object is soft deletable', | ||
}) | ||
export class SetCustomObjectIsSoftDeletableCommand extends ActiveWorkspacesCommandRunner { | ||
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 objectsToUpdate = await this.objectMetadataRepository.find({ | ||
select: ['id'], | ||
where: updateCriteria, | ||
}); | ||
|
||
this.logger.log( | ||
`Dry run: ${objectsToUpdate.length} objects would be updated`, | ||
); | ||
|
||
return; | ||
} | ||
|
||
const result = await this.objectMetadataRepository.update(updateCriteria, { | ||
isSoftDeletable: true, | ||
}); | ||
|
||
this.logger.log(`Updated ${result.affected} objects`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/twenty-server/src/database/typeorm/metadata/metadata.datasource.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters