feat: populate relation join column (#10212)
Fix https://github.com/twentyhq/core-team-issues/issues/241#issue-2793030259
This commit is contained in:
@ -4,19 +4,23 @@ import { WorkspaceActivationStatus } from 'twenty-shared';
|
|||||||
import { In, MoreThanOrEqual, Repository } from 'typeorm';
|
import { In, MoreThanOrEqual, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseCommandOptions,
|
MigrationCommandOptions,
|
||||||
BaseCommandRunner,
|
MigrationCommandRunner,
|
||||||
} from 'src/database/commands/base.command';
|
} from 'src/database/commands/migration-command/migration-command.runner';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
export type ActiveWorkspacesCommandOptions = BaseCommandOptions & {
|
|
||||||
workspaceId?: string;
|
|
||||||
startFromWorkspaceId?: string;
|
|
||||||
workspaceCountLimit?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
|
export type ActiveWorkspacesMigrationCommandOptions =
|
||||||
|
MigrationCommandOptions & {
|
||||||
|
workspaceId?: string;
|
||||||
|
startFromWorkspaceId?: string;
|
||||||
|
workspaceCountLimit?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class ActiveWorkspacesMigrationCommandRunner<
|
||||||
|
Options extends
|
||||||
|
ActiveWorkspacesMigrationCommandOptions = ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
> extends MigrationCommandRunner<Options> {
|
||||||
private workspaceIds: string[] = [];
|
private workspaceIds: string[] = [];
|
||||||
private startFromWorkspaceId: string | undefined;
|
private startFromWorkspaceId: string | undefined;
|
||||||
private workspaceCountLimit: number | undefined;
|
private workspaceCountLimit: number | undefined;
|
||||||
@ -105,9 +109,9 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override async executeBaseCommand(
|
override async runMigrationCommand(
|
||||||
passedParams: string[],
|
passedParams: string[],
|
||||||
options: BaseCommandOptions,
|
options: Options,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const activeWorkspaceIds =
|
const activeWorkspaceIds =
|
||||||
this.workspaceIds.length > 0
|
this.workspaceIds.length > 0
|
||||||
@ -120,64 +124,16 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
|
|||||||
this.logger.log(chalk.yellow('Dry run mode: No changes will be applied'));
|
this.logger.log(chalk.yellow('Dry run mode: No changes will be applied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.executeActiveWorkspacesCommand(
|
await this.runMigrationCommandOnActiveWorkspaces(
|
||||||
passedParams,
|
passedParams,
|
||||||
options,
|
options,
|
||||||
activeWorkspaceIds,
|
activeWorkspaceIds,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async processEachWorkspaceWithWorkspaceDataSource(
|
protected abstract runMigrationCommandOnActiveWorkspaces(
|
||||||
workspaceIds: string[],
|
|
||||||
callback: ({
|
|
||||||
workspaceId,
|
|
||||||
index,
|
|
||||||
total,
|
|
||||||
dataSource,
|
|
||||||
}: {
|
|
||||||
workspaceId: string;
|
|
||||||
index: number;
|
|
||||||
total: number;
|
|
||||||
dataSource: WorkspaceDataSource;
|
|
||||||
}) => Promise<void>,
|
|
||||||
): Promise<void> {
|
|
||||||
this.logger.log(
|
|
||||||
chalk.green(`Running command on ${workspaceIds.length} workspaces`),
|
|
||||||
);
|
|
||||||
for (const [index, workspaceId] of workspaceIds.entries()) {
|
|
||||||
this.logger.log(
|
|
||||||
chalk.green(
|
|
||||||
`Processing workspace ${workspaceId} (${index + 1}/${
|
|
||||||
workspaceIds.length
|
|
||||||
})`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const dataSource =
|
|
||||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(
|
|
||||||
workspaceId,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await callback({
|
|
||||||
workspaceId,
|
|
||||||
index,
|
|
||||||
total: workspaceIds.length,
|
|
||||||
dataSource,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error in workspace ${workspaceId}: ${error}`);
|
|
||||||
}
|
|
||||||
await this.twentyORMGlobalManager.destroyDataSourceForWorkspace(
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract executeActiveWorkspacesCommand(
|
|
||||||
passedParams: string[],
|
passedParams: string[],
|
||||||
options: BaseCommandOptions,
|
options: Options,
|
||||||
activeWorkspaceIds: string[],
|
activeWorkspaceIds: string[],
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||||
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
|
|
||||||
|
export abstract class BatchActiveWorkspacesMigrationCommandRunner<
|
||||||
|
Options extends
|
||||||
|
ActiveWorkspacesMigrationCommandOptions = ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
> extends ActiveWorkspacesMigrationCommandRunner<Options> {
|
||||||
|
constructor(
|
||||||
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
|
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
|
) {
|
||||||
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
|
_passedParams: string[],
|
||||||
|
_options: Options,
|
||||||
|
activeWorkspaceIds: string[],
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log(
|
||||||
|
chalk.green(`Running command on ${activeWorkspaceIds.length} workspaces`),
|
||||||
|
);
|
||||||
|
for (const [index, workspaceId] of activeWorkspaceIds.entries()) {
|
||||||
|
this.logger.log(
|
||||||
|
chalk.green(
|
||||||
|
`Processing workspace ${workspaceId} (${index + 1}/${
|
||||||
|
activeWorkspaceIds.length
|
||||||
|
})`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataSource =
|
||||||
|
await this.twentyORMGlobalManager.getDataSourceForWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.runMigrationCommandOnWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
index,
|
||||||
|
activeWorkspaceIds.length,
|
||||||
|
dataSource,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error in workspace ${workspaceId}: ${error}`);
|
||||||
|
}
|
||||||
|
await this.twentyORMGlobalManager.destroyDataSourceForWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract runMigrationCommandOnWorkspace(
|
||||||
|
workspaceId: string,
|
||||||
|
index: number,
|
||||||
|
total: number,
|
||||||
|
dataSource: WorkspaceDataSource,
|
||||||
|
): Promise<void>;
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { Inject } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { Command } from 'nest-commander';
|
||||||
|
|
||||||
|
import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface';
|
||||||
|
|
||||||
|
import { MIGRATION_COMMAND_INJECTION_TOKEN } from 'src/database/commands/migration-command/migration-command.constants';
|
||||||
|
import { MigrationCommandRunner } from 'src/database/commands/migration-command/migration-command.runner';
|
||||||
|
|
||||||
|
export function createUpgradeAllCommand(
|
||||||
|
version: string,
|
||||||
|
): new (...args: unknown[]) => MigrationCommandRunner {
|
||||||
|
@Command({
|
||||||
|
name: `upgrade-${version}`,
|
||||||
|
description: `Upgrade to version ${version}`,
|
||||||
|
})
|
||||||
|
class UpgradeCommand extends MigrationCommandRunner {
|
||||||
|
constructor(
|
||||||
|
@Inject(MIGRATION_COMMAND_INJECTION_TOKEN)
|
||||||
|
private readonly subCommands: MigrationCommandInterface[],
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async runMigrationCommand(
|
||||||
|
passedParams: string[],
|
||||||
|
options: Record<string, unknown>,
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log(`Running upgrade command for version ${version}`);
|
||||||
|
|
||||||
|
for (const command of this.subCommands) {
|
||||||
|
await command.runMigrationCommand(passedParams, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Upgrade ${version} command completed!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpgradeCommand;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
// migration-command.decorator.ts
|
||||||
|
import { Type } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { Command, CommandMetadata } from 'nest-commander';
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import { MigrationCommandRunner } from 'src/database/commands/migration-command/migration-command.runner';
|
||||||
|
|
||||||
|
export interface MigrationCommandMetadata extends CommandMetadata {
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MIGRATION_COMMANDS = new Map<
|
||||||
|
string,
|
||||||
|
Array<Type<MigrationCommandRunner>>
|
||||||
|
>();
|
||||||
|
|
||||||
|
export function MigrationCommand(
|
||||||
|
options: MigrationCommandMetadata,
|
||||||
|
): <T extends Type<MigrationCommandRunner>>(target: T) => T | void {
|
||||||
|
return <T extends Type<MigrationCommandRunner>>(target: T): T | void => {
|
||||||
|
const { version, name, ...commandOptions } = options;
|
||||||
|
|
||||||
|
if (!MIGRATION_COMMANDS.has(version)) {
|
||||||
|
MIGRATION_COMMANDS.set(version, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
MIGRATION_COMMANDS.get(version)?.push(target);
|
||||||
|
|
||||||
|
return Command({
|
||||||
|
name: `upgrade-${version}:${name}`,
|
||||||
|
...commandOptions,
|
||||||
|
})(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMigrationCommandsForVersion(
|
||||||
|
version: string,
|
||||||
|
): Array<Type<MigrationCommandRunner>> {
|
||||||
|
return MIGRATION_COMMANDS.get(version) || [];
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export interface MigrationCommandInterface<
|
||||||
|
Options extends Record<string, unknown> = Record<string, unknown>,
|
||||||
|
> {
|
||||||
|
runMigrationCommand(passedParams: string[], options: Options): Promise<void>;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const MIGRATION_COMMAND_INJECTION_TOKEN = 'MIGRATION_COMMANDS';
|
||||||
@ -3,14 +3,22 @@ import { Logger } from '@nestjs/common';
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { CommandRunner, Option } from 'nest-commander';
|
import { CommandRunner, Option } from 'nest-commander';
|
||||||
|
|
||||||
import { CommandLogger } from './logger';
|
import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface';
|
||||||
export type BaseCommandOptions = {
|
|
||||||
|
import { CommandLogger } from 'src/database/commands/logger';
|
||||||
|
|
||||||
|
export type MigrationCommandOptions = {
|
||||||
workspaceId?: string;
|
workspaceId?: string;
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class BaseCommandRunner extends CommandRunner {
|
export abstract class MigrationCommandRunner<
|
||||||
|
Options extends MigrationCommandOptions = MigrationCommandOptions,
|
||||||
|
>
|
||||||
|
extends CommandRunner
|
||||||
|
implements MigrationCommandInterface<Options>
|
||||||
|
{
|
||||||
protected logger: CommandLogger | Logger;
|
protected logger: CommandLogger | Logger;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -38,10 +46,7 @@ export abstract class BaseCommandRunner extends CommandRunner {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override async run(
|
override async run(passedParams: string[], options: Options): Promise<void> {
|
||||||
passedParams: string[],
|
|
||||||
options: BaseCommandOptions,
|
|
||||||
): Promise<void> {
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
this.logger = new CommandLogger({
|
this.logger = new CommandLogger({
|
||||||
verbose: true,
|
verbose: true,
|
||||||
@ -50,7 +55,7 @@ export abstract class BaseCommandRunner extends CommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.executeBaseCommand(passedParams, options);
|
await this.runMigrationCommand(passedParams, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(chalk.red(`Command failed`));
|
this.logger.error(chalk.red(`Command failed`));
|
||||||
throw error;
|
throw error;
|
||||||
@ -59,8 +64,8 @@ export abstract class BaseCommandRunner extends CommandRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract executeBaseCommand(
|
abstract runMigrationCommand(
|
||||||
passedParams: string[],
|
passedParams: string[],
|
||||||
options: BaseCommandOptions,
|
options: Options,
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { DynamicModule, Module, ModuleMetadata } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface';
|
||||||
|
|
||||||
|
import { createUpgradeAllCommand } from 'src/database/commands/migration-command/create-upgrade-all-command.factory';
|
||||||
|
import { getMigrationCommandsForVersion } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
|
import { MIGRATION_COMMAND_INJECTION_TOKEN } from 'src/database/commands/migration-command/migration-command.constants';
|
||||||
|
|
||||||
|
@Module({})
|
||||||
|
export class MigrationCommandModule {
|
||||||
|
static register(
|
||||||
|
version: string,
|
||||||
|
moduleMetadata: ModuleMetadata,
|
||||||
|
): DynamicModule {
|
||||||
|
const commandClasses = getMigrationCommandsForVersion(version);
|
||||||
|
const upgradeAllCommand = createUpgradeAllCommand(version);
|
||||||
|
|
||||||
|
return {
|
||||||
|
module: MigrationCommandModule,
|
||||||
|
imports: moduleMetadata.imports,
|
||||||
|
providers: [
|
||||||
|
...(moduleMetadata.providers ?? []),
|
||||||
|
...commandClasses,
|
||||||
|
{
|
||||||
|
provide: MIGRATION_COMMAND_INJECTION_TOKEN,
|
||||||
|
useFactory: (
|
||||||
|
...instances: MigrationCommandInterface[]
|
||||||
|
): MigrationCommandInterface[] => {
|
||||||
|
return instances;
|
||||||
|
},
|
||||||
|
inject: commandClasses,
|
||||||
|
},
|
||||||
|
upgradeAllCommand,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
...(moduleMetadata.exports ?? []),
|
||||||
|
MIGRATION_COMMAND_INJECTION_TOKEN,
|
||||||
|
...commandClasses,
|
||||||
|
upgradeAllCommand,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,11 +4,11 @@ import chalk from 'chalk';
|
|||||||
import { Command } from 'nest-commander';
|
import { Command } from 'nest-commander';
|
||||||
import { In, Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
|
||||||
ActiveWorkspacesCommandOptions,
|
|
||||||
ActiveWorkspacesCommandRunner,
|
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { isCommandLogger } from 'src/database/commands/logger';
|
import { isCommandLogger } from 'src/database/commands/logger';
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||||
@ -20,7 +20,7 @@ import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.work
|
|||||||
name: 'upgrade-0.42:fix-body-v2-view-field-position',
|
name: 'upgrade-0.42:fix-body-v2-view-field-position',
|
||||||
description: 'Make bodyV2 field position to match body field position',
|
description: 'Make bodyV2 field position to match body field position',
|
||||||
})
|
})
|
||||||
export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesCommandRunner {
|
export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -32,9 +32,9 @@ export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesCommandRu
|
|||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: ActiveWorkspacesCommandOptions,
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log('Running command to fix bodyV2 field position');
|
this.logger.log('Running command to fix bodyV2 field position');
|
||||||
|
|||||||
@ -1,25 +1,26 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
|
||||||
ActiveWorkspacesCommandOptions,
|
|
||||||
ActiveWorkspacesCommandRunner,
|
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { CommandLogger } from 'src/database/commands/logger';
|
import { CommandLogger } from 'src/database/commands/logger';
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
import { settings } from 'src/engine/constants/settings';
|
import { settings } from 'src/engine/constants/settings';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
|
||||||
@Command({
|
@MigrationCommand({
|
||||||
name: 'upgrade-0.42:limit-amount-of-view-field',
|
name: 'limit-amount-of-view-field',
|
||||||
description: 'Limit amount of view field.',
|
description: 'Limit amount of view field.',
|
||||||
|
version: '0.42',
|
||||||
})
|
})
|
||||||
export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner {
|
export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
protected readonly logger: CommandLogger;
|
protected readonly logger: CommandLogger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -35,7 +36,7 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner
|
|||||||
this.logger.setVerbose(false);
|
this.logger.setVerbose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(workspaceId: string, dryRun?: boolean): Promise<void> {
|
async runOnWorkspace(workspaceId: string, dryRun?: boolean): Promise<void> {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Processing workspace ${workspaceId} for view field limitation`,
|
`Processing workspace ${workspaceId} for view field limitation`,
|
||||||
);
|
);
|
||||||
@ -94,9 +95,9 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: ActiveWorkspacesCommandOptions,
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log(`Running limit-amount-of-view-field command`);
|
this.logger.log(`Running limit-amount-of-view-field command`);
|
||||||
@ -107,7 +108,7 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner
|
|||||||
|
|
||||||
for (const [index, workspaceId] of workspaceIds.entries()) {
|
for (const [index, workspaceId] of workspaceIds.entries()) {
|
||||||
try {
|
try {
|
||||||
await this.execute(workspaceId, options?.dryRun);
|
await this.runOnWorkspace(workspaceId, options?.dryRun);
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
`Processed workspace: ${workspaceId} (${index + 1}/${
|
`Processed workspace: ${workspaceId} (${index + 1}/${
|
||||||
workspaceIds.length
|
workspaceIds.length
|
||||||
|
|||||||
@ -2,13 +2,16 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { ServerBlockNoteEditor } from '@blocknote/server-util';
|
import { ServerBlockNoteEditor } from '@blocknote/server-util';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command, Option } from 'nest-commander';
|
import { Option } from 'nest-commander';
|
||||||
import { FieldMetadataType, isDefined } from 'twenty-shared';
|
import { FieldMetadataType, isDefined } from 'twenty-shared';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { isCommandLogger } from 'src/database/commands/logger';
|
import { isCommandLogger } from 'src/database/commands/logger';
|
||||||
import { Upgrade042CommandOptions } from 'src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command';
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
@ -55,12 +58,19 @@ type ProcessRichTextFieldsArgs = {
|
|||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Command({
|
type MigrateRichTextFieldCommandOptions =
|
||||||
name: 'upgrade-0.42:migrate-rich-text-field',
|
ActiveWorkspacesMigrationCommandOptions & {
|
||||||
|
force?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
@MigrationCommand({
|
||||||
|
name: 'migrate-rich-text-field',
|
||||||
description: 'Migrate RICH_TEXT fields to new composite structure',
|
description: 'Migrate RICH_TEXT fields to new composite structure',
|
||||||
|
version: '0.42',
|
||||||
})
|
})
|
||||||
export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner {
|
export class MigrateRichTextFieldCommand extends ActiveWorkspacesMigrationCommandRunner<MigrateRichTextFieldCommandOptions> {
|
||||||
private options: Upgrade042CommandOptions;
|
private options: MigrateRichTextFieldCommandOptions;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -89,9 +99,9 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner {
|
|||||||
return val ?? false;
|
return val ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: Upgrade042CommandOptions,
|
options: MigrateRichTextFieldCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
@ -343,7 +353,7 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner {
|
|||||||
});
|
});
|
||||||
|
|
||||||
richTextFieldsWithHasCreatedColumns.push({
|
richTextFieldsWithHasCreatedColumns.push({
|
||||||
hasCreatedColumns,
|
hasCreatedColumns: hasCreatedColumns ?? false,
|
||||||
richTextField,
|
richTextField,
|
||||||
objectMetadata,
|
objectMetadata,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,25 +1,26 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
import { IsNull, Repository } from 'typeorm';
|
import { IsNull, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
|
||||||
ActiveWorkspacesCommandOptions,
|
|
||||||
ActiveWorkspacesCommandRunner,
|
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { CommandLogger } from 'src/database/commands/logger';
|
import { CommandLogger } from 'src/database/commands/logger';
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
|
|
||||||
@Command({
|
@MigrationCommand({
|
||||||
name: 'upgrade-0.42:standardization-of-actor-composite-context-type',
|
name: 'standardization-of-actor-composite-context-type',
|
||||||
description: 'Add context to actor composite type.',
|
description: 'Add context to actor composite type.',
|
||||||
|
version: '0.42',
|
||||||
})
|
})
|
||||||
export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWorkspacesCommandRunner {
|
export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
protected readonly logger;
|
protected readonly logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -39,9 +40,9 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor
|
|||||||
this.logger.setVerbose(false);
|
this.logger.setVerbose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: ActiveWorkspacesCommandOptions,
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log(`Running add-context-to-actor-composite-type command`);
|
this.logger.log(`Running add-context-to-actor-composite-type command`);
|
||||||
@ -52,7 +53,7 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor
|
|||||||
|
|
||||||
for (const [index, workspaceId] of workspaceIds.entries()) {
|
for (const [index, workspaceId] of workspaceIds.entries()) {
|
||||||
try {
|
try {
|
||||||
await this.execute(workspaceId, options?.dryRun);
|
await this.runOnWorkspace(workspaceId, options?.dryRun);
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
`[${index + 1}/${workspaceIds.length}] Added for workspace: ${workspaceId}`,
|
`[${index + 1}/${workspaceIds.length}] Added for workspace: ${workspaceId}`,
|
||||||
);
|
);
|
||||||
@ -62,7 +63,10 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async execute(workspaceId: string, dryRun = false): Promise<void> {
|
private async runOnWorkspace(
|
||||||
|
workspaceId: string,
|
||||||
|
dryRun = false,
|
||||||
|
): Promise<void> {
|
||||||
this.logger.verbose(`Adding for workspace: ${workspaceId}`);
|
this.logger.verbose(`Adding for workspace: ${workspaceId}`);
|
||||||
const actorFields = await this.fieldMetadataRepository.find({
|
const actorFields = await this.fieldMetadataRepository.find({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { Command, Option } from 'nest-commander';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { BaseCommandOptions } from 'src/database/commands/base.command';
|
|
||||||
import { FixBodyV2ViewFieldPositionCommand } from 'src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command';
|
|
||||||
import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field';
|
|
||||||
import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command';
|
|
||||||
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
|
||||||
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
|
|
||||||
|
|
||||||
type Upgrade042CommandCustomOptions = {
|
|
||||||
force: boolean;
|
|
||||||
};
|
|
||||||
export type Upgrade042CommandOptions = BaseCommandOptions &
|
|
||||||
Upgrade042CommandCustomOptions;
|
|
||||||
@Command({
|
|
||||||
name: 'upgrade-0.42',
|
|
||||||
description: 'Upgrade to 0.42',
|
|
||||||
})
|
|
||||||
export class UpgradeTo0_42Command extends ActiveWorkspacesCommandRunner {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(Workspace, 'core')
|
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
|
||||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
|
||||||
private readonly migrateRichTextFieldCommand: MigrateRichTextFieldCommand,
|
|
||||||
private readonly fixBodyV2ViewFieldPositionCommand: FixBodyV2ViewFieldPositionCommand,
|
|
||||||
private readonly limitAmountOfViewFieldCommand: LimitAmountOfViewFieldCommand,
|
|
||||||
private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand,
|
|
||||||
private readonly standardizationOfActorCompositeContextType: StandardizationOfActorCompositeContextTypeCommand,
|
|
||||||
) {
|
|
||||||
super(workspaceRepository, twentyORMGlobalManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Option({
|
|
||||||
flags: '-f, --force [boolean]',
|
|
||||||
description:
|
|
||||||
'Force RICH_TEXT_FIELD value update even if column migration has already be run',
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
parseForceValue(val?: boolean): boolean {
|
|
||||||
return val ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
|
||||||
passedParam: string[],
|
|
||||||
options: Upgrade042CommandOptions,
|
|
||||||
workspaceIds: string[],
|
|
||||||
): Promise<void> {
|
|
||||||
this.logger.log('Running command to upgrade to 0.42');
|
|
||||||
|
|
||||||
await this.migrateRichTextFieldCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.fixBodyV2ViewFieldPositionCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.limitAmountOfViewFieldCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.syncWorkspaceMetadataCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.standardizationOfActorCompositeContextType.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { MigrationCommandModule } from 'src/database/commands/migration-command/miration-command.module';
|
||||||
import { FixBodyV2ViewFieldPositionCommand } from 'src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command';
|
import { FixBodyV2ViewFieldPositionCommand } from 'src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command';
|
||||||
import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field';
|
import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field';
|
||||||
import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command';
|
import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command';
|
||||||
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
||||||
import { UpgradeTo0_42Command } from 'src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command';
|
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
@ -19,24 +19,27 @@ import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manage
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
|
MigrationCommandModule.register('0.42', {
|
||||||
TypeOrmModule.forFeature(
|
imports: [
|
||||||
[ObjectMetadataEntity, FieldMetadataEntity],
|
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
|
||||||
'metadata',
|
TypeOrmModule.forFeature(
|
||||||
),
|
[ObjectMetadataEntity, FieldMetadataEntity],
|
||||||
WorkspaceSyncMetadataCommandsModule,
|
'metadata',
|
||||||
WorkspaceMigrationRunnerModule,
|
),
|
||||||
WorkspaceMigrationModule,
|
WorkspaceSyncMetadataCommandsModule,
|
||||||
WorkspaceMetadataVersionModule,
|
WorkspaceMigrationRunnerModule,
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceMigrationModule,
|
||||||
FeatureFlagModule,
|
WorkspaceMetadataVersionModule,
|
||||||
],
|
WorkspaceDataSourceModule,
|
||||||
providers: [
|
FeatureFlagModule,
|
||||||
UpgradeTo0_42Command,
|
],
|
||||||
MigrateRichTextFieldCommand,
|
providers: [
|
||||||
FixBodyV2ViewFieldPositionCommand,
|
MigrateRichTextFieldCommand,
|
||||||
LimitAmountOfViewFieldCommand,
|
FixBodyV2ViewFieldPositionCommand,
|
||||||
StandardizationOfActorCompositeContextTypeCommand,
|
LimitAmountOfViewFieldCommand,
|
||||||
|
StandardizationOfActorCompositeContextTypeCommand,
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UpgradeTo0_42CommandModule {}
|
export class UpgradeTo0_42CommandModule {}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import {
|
|
||||||
ActiveWorkspacesCommandOptions,
|
|
||||||
ActiveWorkspacesCommandRunner,
|
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { isCommandLogger } from 'src/database/commands/logger';
|
import { isCommandLogger } from 'src/database/commands/logger';
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
import { FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
@ -24,11 +24,12 @@ import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/vie
|
|||||||
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
|
||||||
@Command({
|
@MigrationCommand({
|
||||||
name: 'upgrade-0.43:add-tasks-assigned-to-me-view',
|
name: 'add-tasks-assigned-to-me-view',
|
||||||
description: 'Add tasks assigned to me view',
|
description: 'Add tasks assigned to me view',
|
||||||
|
version: '0.43',
|
||||||
})
|
})
|
||||||
export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesCommandRunner {
|
export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -42,9 +43,9 @@ export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesCommandRunn
|
|||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: ActiveWorkspacesCommandOptions,
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log('Running command to create many to one relations');
|
this.logger.log('Running command to create many to one relations');
|
||||||
|
|||||||
@ -0,0 +1,169 @@
|
|||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
|
||||||
|
import { isCommandLogger } from 'src/database/commands/logger';
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
|
import {
|
||||||
|
RelationDirection,
|
||||||
|
deduceRelationDirection,
|
||||||
|
} from 'src/engine/utils/deduce-relation-direction.util';
|
||||||
|
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||||
|
|
||||||
|
@MigrationCommand({
|
||||||
|
name: 'migrate-relations-to-field-metadata',
|
||||||
|
description: 'Migrate relations to field metadata',
|
||||||
|
version: '0.43',
|
||||||
|
})
|
||||||
|
export class MigrateRelationsToFieldMetadataCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
|
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||||
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
|
) {
|
||||||
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
|
_passedParam: string[],
|
||||||
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
workspaceIds: string[],
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log('Running command to create many to one relations');
|
||||||
|
|
||||||
|
if (isCommandLogger(this.logger)) {
|
||||||
|
this.logger.setVerbose(options.verbose ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const [index, workspaceId] of workspaceIds.entries()) {
|
||||||
|
await this.processWorkspace(workspaceId, index, workspaceIds.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(chalk.green('Command completed!'));
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.log(chalk.red('Error in workspace'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processWorkspace(
|
||||||
|
workspaceId: string,
|
||||||
|
index: number,
|
||||||
|
total: number,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.logger.log(
|
||||||
|
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldMetadataCollection = await this.fieldMetadataRepository.find({
|
||||||
|
where: {
|
||||||
|
workspaceId,
|
||||||
|
type: In([FieldMetadataType.RELATION, FieldMetadataType.UUID]),
|
||||||
|
},
|
||||||
|
relations: ['fromRelationMetadata', 'toRelationMetadata'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!fieldMetadataCollection.length) {
|
||||||
|
this.logger.log(
|
||||||
|
chalk.yellow(
|
||||||
|
`No relation field metadata found for workspace ${workspaceId}.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinColumnFieldMetadataCollection = fieldMetadataCollection.filter(
|
||||||
|
(fieldMetadata) =>
|
||||||
|
isFieldMetadataOfType(fieldMetadata, FieldMetadataType.UUID),
|
||||||
|
// TODO: Fix this, it's working in other places but not here
|
||||||
|
) as FieldMetadataEntity<FieldMetadataType.UUID>[];
|
||||||
|
|
||||||
|
const fieldMetadataToUpdateCollection = fieldMetadataCollection
|
||||||
|
.filter((fieldMetadata) =>
|
||||||
|
isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION),
|
||||||
|
)
|
||||||
|
.map((fieldMetadata) =>
|
||||||
|
this.updateRelationFieldMetadata(
|
||||||
|
joinColumnFieldMetadataCollection,
|
||||||
|
// TODO: Fix this, it's working in other places but not here
|
||||||
|
fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fieldMetadataToUpdateCollection.length > 0) {
|
||||||
|
await this.fieldMetadataRepository.save(
|
||||||
|
fieldMetadataToUpdateCollection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
chalk.green(`Command completed for workspace ${workspaceId}.`),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
this.logger.log(chalk.red(`Error in workspace ${workspaceId}.`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateRelationFieldMetadata(
|
||||||
|
joinColumnFieldMetadataCollection: FieldMetadataEntity<FieldMetadataType.UUID>[],
|
||||||
|
fieldMetadata: FieldMetadataEntity<FieldMetadataType.RELATION>,
|
||||||
|
): FieldMetadataEntity<FieldMetadataType.RELATION> {
|
||||||
|
const relationMetadata =
|
||||||
|
fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata;
|
||||||
|
const joinColumnFieldMetadata = joinColumnFieldMetadataCollection.find(
|
||||||
|
(joinColumnFieldMetadata) =>
|
||||||
|
// We're deducing the field based on the name of the relation field
|
||||||
|
// This is not the best way to do this but we don't have a better way
|
||||||
|
joinColumnFieldMetadata.name === `${fieldMetadata.name}Id`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const relationDirection = deduceRelationDirection(
|
||||||
|
fieldMetadata,
|
||||||
|
relationMetadata,
|
||||||
|
);
|
||||||
|
let relationType = relationMetadata.relationType as unknown as RelationType;
|
||||||
|
|
||||||
|
if (
|
||||||
|
relationDirection === RelationDirection.TO &&
|
||||||
|
relationType === RelationType.ONE_TO_MANY
|
||||||
|
) {
|
||||||
|
relationType = RelationType.MANY_TO_ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationTargetFieldMetadataId =
|
||||||
|
relationDirection === RelationDirection.FROM
|
||||||
|
? relationMetadata.toFieldMetadataId
|
||||||
|
: relationMetadata.fromFieldMetadataId;
|
||||||
|
|
||||||
|
const relationTargetObjectMetadataId =
|
||||||
|
relationDirection === RelationDirection.FROM
|
||||||
|
? relationMetadata.toObjectMetadataId
|
||||||
|
: relationMetadata.fromObjectMetadataId;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...fieldMetadata,
|
||||||
|
settings: {
|
||||||
|
relationType,
|
||||||
|
onDelete: relationMetadata.onDeleteAction,
|
||||||
|
joinColumnName: joinColumnFieldMetadata?.name,
|
||||||
|
},
|
||||||
|
relationTargetFieldMetadataId,
|
||||||
|
relationTargetObjectMetadataId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveWorkspacesCommandOptions,
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
ActiveWorkspacesCommandRunner,
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
@ -19,11 +19,12 @@ import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/wo
|
|||||||
import { SEARCH_FIELDS_FOR_NOTES } from 'src/modules/note/standard-objects/note.workspace-entity';
|
import { SEARCH_FIELDS_FOR_NOTES } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||||
import { SEARCH_FIELDS_FOR_TASKS } from 'src/modules/task/standard-objects/task.workspace-entity';
|
import { SEARCH_FIELDS_FOR_TASKS } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||||
|
|
||||||
@Command({
|
@MigrationCommand({
|
||||||
name: 'upgrade-0.43:migrate-search-vector-on-note-and-task-entities',
|
name: 'migrate-search-vector-on-note-and-task-entities',
|
||||||
description: 'Migrate search vector on note and task entities',
|
description: 'Migrate search vector on note and task entities',
|
||||||
|
version: '0.43',
|
||||||
})
|
})
|
||||||
export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorkspacesCommandRunner {
|
export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -39,9 +40,9 @@ export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorks
|
|||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: ActiveWorkspacesCommandOptions,
|
options: ActiveWorkspacesMigrationCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
|
|||||||
@ -1,59 +1,34 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { In, Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import { BatchActiveWorkspacesMigrationCommandRunner } from 'src/database/commands/migration-command/batch-active-workspaces-migration-command.runner';
|
||||||
ActiveWorkspacesCommandOptions,
|
import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator';
|
||||||
ActiveWorkspacesCommandRunner,
|
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
|
||||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||||
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
|
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
|
||||||
@Command({
|
@MigrationCommand({
|
||||||
name: 'upgrade-0.43:update-default-view-record-opening-on-workflow-objects',
|
name: 'update-default-view-record-opening-on-workflow-objects',
|
||||||
description:
|
description:
|
||||||
'Update default view record opening on workflow objects to record page',
|
'Update default view record opening on workflow objects to record page',
|
||||||
|
version: '0.43',
|
||||||
})
|
})
|
||||||
export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends ActiveWorkspacesCommandRunner {
|
export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends BatchActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
protected readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
protected readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
|
||||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
|
||||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
) {
|
) {
|
||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnWorkspace(
|
||||||
_passedParam: string[],
|
|
||||||
_options: ActiveWorkspacesCommandOptions,
|
|
||||||
workspaceIds: string[],
|
|
||||||
): Promise<void> {
|
|
||||||
this.logger.log(
|
|
||||||
'Running command to update default view record opening on workflow objects to record page',
|
|
||||||
);
|
|
||||||
|
|
||||||
this.processEachWorkspaceWithWorkspaceDataSource(
|
|
||||||
workspaceIds,
|
|
||||||
async ({ workspaceId, index, total }) => {
|
|
||||||
await this.processWorkspace(workspaceId, index, total);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
this.logger.log(chalk.green('Command completed!'));
|
|
||||||
}
|
|
||||||
|
|
||||||
async processWorkspace(
|
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
index: number,
|
index: number,
|
||||||
total: number,
|
total: number,
|
||||||
|
|||||||
@ -1,64 +0,0 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { Command } from 'nest-commander';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
|
||||||
import { BaseCommandOptions } from 'src/database/commands/base.command';
|
|
||||||
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
|
||||||
import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command';
|
|
||||||
import { MigrateSearchVectorOnNoteAndTaskEntitiesCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command';
|
|
||||||
import { UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand } from 'src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
|
||||||
|
|
||||||
@Command({
|
|
||||||
name: 'upgrade-0.43',
|
|
||||||
description: 'Upgrade to 0.43',
|
|
||||||
})
|
|
||||||
export class UpgradeTo0_43Command extends ActiveWorkspacesCommandRunner {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(Workspace, 'core')
|
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
|
||||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
|
||||||
private readonly addTasksAssignedToMeViewCommand: AddTasksAssignedToMeViewCommand,
|
|
||||||
private readonly migrateSearchVectorOnNoteAndTaskEntitiesCommand: MigrateSearchVectorOnNoteAndTaskEntitiesCommand,
|
|
||||||
private readonly updateDefaultViewRecordOpeningOnWorkflowObjectsCommand: UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand,
|
|
||||||
private readonly standardizationOfActorCompositeContextTypeCommand: StandardizationOfActorCompositeContextTypeCommand,
|
|
||||||
) {
|
|
||||||
super(workspaceRepository, twentyORMGlobalManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
|
||||||
passedParam: string[],
|
|
||||||
options: BaseCommandOptions,
|
|
||||||
workspaceIds: string[],
|
|
||||||
): Promise<void> {
|
|
||||||
this.logger.log('Running command to upgrade to 0.43');
|
|
||||||
|
|
||||||
await this.addTasksAssignedToMeViewCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.migrateSearchVectorOnNoteAndTaskEntitiesCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.updateDefaultViewRecordOpeningOnWorkflowObjectsCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note: Introduced in 0.42, ran manually on prod. Introduced to self-host globally on 0.43
|
|
||||||
await this.standardizationOfActorCompositeContextTypeCommand.executeActiveWorkspacesCommand(
|
|
||||||
passedParam,
|
|
||||||
options,
|
|
||||||
workspaceIds,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { MigrationCommandModule } from 'src/database/commands/migration-command/miration-command.module';
|
||||||
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type';
|
||||||
import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command';
|
import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command';
|
||||||
|
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command';
|
||||||
import { MigrateSearchVectorOnNoteAndTaskEntitiesCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command';
|
import { MigrateSearchVectorOnNoteAndTaskEntitiesCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command';
|
||||||
import { UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand } from 'src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command';
|
import { UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand } from 'src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command';
|
||||||
import { UpgradeTo0_43Command } from 'src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command';
|
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
@ -17,22 +18,26 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
|
MigrationCommandModule.register('0.43', {
|
||||||
TypeOrmModule.forFeature(
|
imports: [
|
||||||
[ObjectMetadataEntity, FieldMetadataEntity],
|
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
|
||||||
'metadata',
|
TypeOrmModule.forFeature(
|
||||||
),
|
[ObjectMetadataEntity, FieldMetadataEntity],
|
||||||
SearchModule,
|
'metadata',
|
||||||
WorkspaceMigrationRunnerModule,
|
),
|
||||||
WorkspaceMigrationModule,
|
SearchModule,
|
||||||
WorkspaceMetadataVersionModule,
|
WorkspaceMigrationRunnerModule,
|
||||||
],
|
WorkspaceMigrationModule,
|
||||||
providers: [
|
WorkspaceMetadataVersionModule,
|
||||||
UpgradeTo0_43Command,
|
],
|
||||||
AddTasksAssignedToMeViewCommand,
|
providers: [
|
||||||
MigrateSearchVectorOnNoteAndTaskEntitiesCommand,
|
AddTasksAssignedToMeViewCommand,
|
||||||
UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand,
|
MigrateSearchVectorOnNoteAndTaskEntitiesCommand,
|
||||||
StandardizationOfActorCompositeContextTypeCommand,
|
UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand,
|
||||||
|
StandardizationOfActorCompositeContextTypeCommand,
|
||||||
|
MigrateRelationsToFieldMetadataCommand,
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UpgradeTo0_43CommandModule {}
|
export class UpgradeTo0_43CommandModule {}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
import {
|
import {
|
||||||
DataSource,
|
DataSource,
|
||||||
FindOptionsRelations,
|
FindOptionsRelations,
|
||||||
@ -22,7 +23,7 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ
|
|||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
|
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProcessNestedRelationsV2Helper {
|
export class ProcessNestedRelationsV2Helper {
|
||||||
@ -96,7 +97,9 @@ export class ProcessNestedRelationsV2Helper {
|
|||||||
const sourceFieldMetadata =
|
const sourceFieldMetadata =
|
||||||
parentObjectMetadataItem.fieldsByName[sourceFieldName];
|
parentObjectMetadataItem.fieldsByName[sourceFieldName];
|
||||||
|
|
||||||
if (!isRelationFieldMetadata(sourceFieldMetadata)) {
|
if (
|
||||||
|
!isFieldMetadataOfType(sourceFieldMetadata, FieldMetadataType.RELATION)
|
||||||
|
) {
|
||||||
// TODO: Maybe we should throw an error here ?
|
// TODO: Maybe we should throw an error here ?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
import { isDefined } from 'twenty-shared';
|
import { FieldMetadataType, isDefined } from 'twenty-shared';
|
||||||
|
|
||||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||||
@ -21,7 +21,7 @@ import { CompositeInputTypeDefinitionFactory } from 'src/engine/api/graphql/work
|
|||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||||
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
|
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||||
|
|
||||||
// TODO: find a way to prevent conflict between handlers executing logic on object relations
|
// TODO: find a way to prevent conflict between handlers executing logic on object relations
|
||||||
// And this factory that is also executing logic on object relations
|
// And this factory that is also executing logic on object relations
|
||||||
@ -151,7 +151,9 @@ export class QueryResultGettersFactory {
|
|||||||
objectMetadataMapItem.fieldsByName[recordFieldName],
|
objectMetadataMapItem.fieldsByName[recordFieldName],
|
||||||
)
|
)
|
||||||
.filter(isDefined)
|
.filter(isDefined)
|
||||||
.filter((fieldMetadata) => isRelationFieldMetadata(fieldMetadata));
|
.filter((fieldMetadata) =>
|
||||||
|
isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION),
|
||||||
|
);
|
||||||
|
|
||||||
const relationFieldsProcessedMap = {} as Record<
|
const relationFieldsProcessedMap = {} as Record<
|
||||||
string,
|
string,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
GraphQLFieldConfigMap,
|
GraphQLFieldConfigMap,
|
||||||
GraphQLObjectType,
|
GraphQLObjectType,
|
||||||
} from 'graphql';
|
} from 'graphql';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||||
@ -14,7 +15,7 @@ import { RelationTypeV2Factory } from 'src/engine/api/graphql/workspace-schema-b
|
|||||||
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
|
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
|
||||||
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
||||||
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
|
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
|
||||||
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
|
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||||
|
|
||||||
import { ArgsFactory } from './args.factory';
|
import { ArgsFactory } from './args.factory';
|
||||||
|
|
||||||
@ -107,7 +108,7 @@ export class ExtendObjectTypeDefinitionV2Factory {
|
|||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Ignore non-relation fields as they are already defined
|
// Ignore non-relation fields as they are already defined
|
||||||
if (!isRelationFieldMetadata(fieldMetadata)) {
|
if (!isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export interface TypeOptions<T = any> {
|
|||||||
isArray?: boolean;
|
isArray?: boolean;
|
||||||
arrayDepth?: number;
|
arrayDepth?: number;
|
||||||
defaultValue?: T;
|
defaultValue?: T;
|
||||||
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>;
|
settings?: FieldMetadataSettings<FieldMetadataType>;
|
||||||
isIdField?: boolean;
|
isIdField?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const StringArrayScalarType = new GraphQLList(GraphQLString);
|
|||||||
export class TypeMapperService {
|
export class TypeMapperService {
|
||||||
mapToScalarType(
|
mapToScalarType(
|
||||||
fieldMetadataType: FieldMetadataType,
|
fieldMetadataType: FieldMetadataType,
|
||||||
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>,
|
settings?: FieldMetadataSettings<FieldMetadataType>,
|
||||||
isIdField?: boolean,
|
isIdField?: boolean,
|
||||||
): GraphQLScalarType | undefined {
|
): GraphQLScalarType | undefined {
|
||||||
if (isIdField || settings?.isForeignKey) {
|
if (isIdField || settings?.isForeignKey) {
|
||||||
@ -90,7 +90,7 @@ export class TypeMapperService {
|
|||||||
|
|
||||||
mapToFilterType(
|
mapToFilterType(
|
||||||
fieldMetadataType: FieldMetadataType,
|
fieldMetadataType: FieldMetadataType,
|
||||||
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>,
|
settings?: FieldMetadataSettings<FieldMetadataType>,
|
||||||
isIdField?: boolean,
|
isIdField?: boolean,
|
||||||
): GraphQLInputObjectType | GraphQLScalarType | undefined {
|
): GraphQLInputObjectType | GraphQLScalarType | undefined {
|
||||||
if (isIdField || settings?.isForeignKey) {
|
if (isIdField || settings?.isForeignKey) {
|
||||||
|
|||||||
@ -7,22 +7,22 @@ import { Command } from 'nest-commander';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveWorkspacesCommandOptions,
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
ActiveWorkspacesCommandRunner,
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
} from 'src/database/commands/active-workspaces.command';
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
|
import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
|
||||||
import { StripeSubscriptionService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription.service';
|
import { StripeSubscriptionService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription.service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
|
|
||||||
interface SyncCustomerDataCommandOptions
|
interface SyncCustomerDataCommandOptions
|
||||||
extends ActiveWorkspacesCommandOptions {}
|
extends ActiveWorkspacesMigrationCommandOptions {}
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'billing:sync-customer-data',
|
name: 'billing:sync-customer-data',
|
||||||
description: 'Sync customer data from Stripe for all active workspaces',
|
description: 'Sync customer data from Stripe for all active workspaces',
|
||||||
})
|
})
|
||||||
export class BillingSyncCustomerDataCommand extends ActiveWorkspacesCommandRunner {
|
export class BillingSyncCustomerDataCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -34,7 +34,7 @@ export class BillingSyncCustomerDataCommand extends ActiveWorkspacesCommandRunne
|
|||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: SyncCustomerDataCommandOptions,
|
options: SyncCustomerDataCommandOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import Stripe from 'stripe';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseCommandOptions,
|
MigrationCommandOptions,
|
||||||
BaseCommandRunner,
|
MigrationCommandRunner,
|
||||||
} from 'src/database/commands/base.command';
|
} from 'src/database/commands/migration-command/migration-command.runner';
|
||||||
import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity';
|
import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity';
|
||||||
import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity';
|
import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity';
|
||||||
import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity';
|
import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity';
|
||||||
@ -25,7 +25,7 @@ import { transformStripeProductToDatabaseProduct } from 'src/engine/core-modules
|
|||||||
description:
|
description:
|
||||||
'Fetches from stripe the plans data (meter, product and price) and upserts it into the database',
|
'Fetches from stripe the plans data (meter, product and price) and upserts it into the database',
|
||||||
})
|
})
|
||||||
export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
export class BillingSyncPlansDataCommand extends MigrationCommandRunner {
|
||||||
private readonly batchSize = 5;
|
private readonly batchSize = 5;
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(BillingPrice, 'core')
|
@InjectRepository(BillingPrice, 'core')
|
||||||
@ -43,7 +43,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
|||||||
|
|
||||||
private async upsertMetersRepositoryData(
|
private async upsertMetersRepositoryData(
|
||||||
meters: Stripe.Billing.Meter[],
|
meters: Stripe.Billing.Meter[],
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
) {
|
) {
|
||||||
meters.map(async (meter) => {
|
meters.map(async (meter) => {
|
||||||
try {
|
try {
|
||||||
@ -64,7 +64,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
|||||||
|
|
||||||
private async upsertProductRepositoryData(
|
private async upsertProductRepositoryData(
|
||||||
product: Stripe.Product,
|
product: Stripe.Product,
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (!options.dryRun) {
|
if (!options.dryRun) {
|
||||||
@ -83,7 +83,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
|||||||
|
|
||||||
private async getBillingPrices(
|
private async getBillingPrices(
|
||||||
products: Stripe.Product[],
|
products: Stripe.Product[],
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
): Promise<Stripe.Price[][]> {
|
): Promise<Stripe.Price[][]> {
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
products.map(async (product) => {
|
products.map(async (product) => {
|
||||||
@ -113,7 +113,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
|||||||
|
|
||||||
private async processBillingPricesByProductBatches(
|
private async processBillingPricesByProductBatches(
|
||||||
products: Stripe.Product[],
|
products: Stripe.Product[],
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
) {
|
) {
|
||||||
const prices: Stripe.Price[][] = [];
|
const prices: Stripe.Price[][] = [];
|
||||||
|
|
||||||
@ -135,9 +135,9 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner {
|
|||||||
return prices;
|
return prices;
|
||||||
}
|
}
|
||||||
|
|
||||||
override async executeBaseCommand(
|
override async runMigrationCommand(
|
||||||
passedParams: string[],
|
passedParams: string[],
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const billingMeters = await this.stripeBillingMeterService.getAllMeters();
|
const billingMeters = await this.stripeBillingMeterService.getAllMeters();
|
||||||
|
|
||||||
|
|||||||
@ -64,9 +64,7 @@ registerEnumType(FieldMetadataType, {
|
|||||||
@Relation('object', () => ObjectMetadataDTO, {
|
@Relation('object', () => ObjectMetadataDTO, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
export class FieldMetadataDTO<
|
export class FieldMetadataDTO<T extends FieldMetadataType = FieldMetadataType> {
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
|
||||||
> {
|
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IDField(() => UUIDScalarType)
|
@IDField(() => UUIDScalarType)
|
||||||
@ -75,7 +73,7 @@ export class FieldMetadataDTO<
|
|||||||
@IsEnum(FieldMetadataType)
|
@IsEnum(FieldMetadataType)
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@Field(() => FieldMetadataType)
|
@Field(() => FieldMetadataType)
|
||||||
type: FieldMetadataType;
|
type: T;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class TextSettingsValidation {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FieldMetadataValidationService<
|
export class FieldMetadataValidationService<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> {
|
> {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-met
|
|||||||
'relationTargetObjectMetadataId',
|
'relationTargetObjectMetadataId',
|
||||||
])
|
])
|
||||||
export class FieldMetadataEntity<
|
export class FieldMetadataEntity<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> implements FieldMetadataInterface<T>
|
> implements FieldMetadataInterface<T>
|
||||||
{
|
{
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
@ -59,7 +59,7 @@ export class FieldMetadataEntity<
|
|||||||
nullable: false,
|
nullable: false,
|
||||||
type: 'varchar',
|
type: 'varchar',
|
||||||
})
|
})
|
||||||
type: FieldMetadataType;
|
type: T;
|
||||||
|
|
||||||
@Column({ nullable: false })
|
@Column({ nullable: false })
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType, IsExactly } from 'twenty-shared';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FieldMetadataDefaultActor,
|
FieldMetadataDefaultActor,
|
||||||
@ -60,17 +60,14 @@ export type FieldMetadataFunctionDefaultValue = ExtractValueType<
|
|||||||
FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction
|
FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
|
||||||
T,
|
|
||||||
] extends [keyof FieldMetadataDefaultValueMapping]
|
|
||||||
? ExtractValueType<FieldMetadataDefaultValueMapping[T]> | null
|
|
||||||
: T extends 'default'
|
|
||||||
? ExtractValueType<UnionOfValues<FieldMetadataDefaultValueMapping>> | null
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type FieldMetadataDefaultValue<
|
export type FieldMetadataDefaultValue<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> = DefaultValueByFieldMetadata<T>;
|
> =
|
||||||
|
IsExactly<T, FieldMetadataType> extends true
|
||||||
|
? ExtractValueType<UnionOfValues<FieldMetadataDefaultValueMapping>> | null
|
||||||
|
: T extends keyof FieldMetadataDefaultValueMapping
|
||||||
|
? ExtractValueType<FieldMetadataDefaultValueMapping[T]> | null
|
||||||
|
: never;
|
||||||
|
|
||||||
type FieldMetadataDefaultValueExtractedTypes = {
|
type FieldMetadataDefaultValueExtractedTypes = {
|
||||||
[K in keyof FieldMetadataDefaultValueMapping]: ExtractValueType<
|
[K in keyof FieldMetadataDefaultValueMapping]: ExtractValueType<
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType, IsExactly } from 'twenty-shared';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FieldMetadataComplexOption,
|
FieldMetadataComplexOption,
|
||||||
@ -11,13 +11,11 @@ type FieldMetadataOptionsMapping = {
|
|||||||
[FieldMetadataType.MULTI_SELECT]: FieldMetadataComplexOption[];
|
[FieldMetadataType.MULTI_SELECT]: FieldMetadataComplexOption[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type OptionsByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
|
||||||
T extends keyof FieldMetadataOptionsMapping
|
|
||||||
? FieldMetadataOptionsMapping[T]
|
|
||||||
: T extends 'default'
|
|
||||||
? FieldMetadataDefaultOption[] | FieldMetadataComplexOption[]
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type FieldMetadataOptions<
|
export type FieldMetadataOptions<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> = OptionsByFieldMetadata<T>;
|
> =
|
||||||
|
IsExactly<T, FieldMetadataType> extends true
|
||||||
|
? FieldMetadataDefaultOption[] | FieldMetadataComplexOption[]
|
||||||
|
: T extends keyof FieldMetadataOptionsMapping
|
||||||
|
? FieldMetadataOptionsMapping[T]
|
||||||
|
: never;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType, IsExactly } from 'twenty-shared';
|
||||||
|
|
||||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
||||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
@ -36,6 +36,7 @@ export type FieldMetadataDateTimeSettings = {
|
|||||||
export type FieldMetadataRelationSettings = {
|
export type FieldMetadataRelationSettings = {
|
||||||
relationType: RelationType;
|
relationType: RelationType;
|
||||||
onDelete?: RelationOnDeleteAction;
|
onDelete?: RelationOnDeleteAction;
|
||||||
|
joinColumnName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FieldMetadataSettingsMapping = {
|
type FieldMetadataSettingsMapping = {
|
||||||
@ -46,13 +47,11 @@ type FieldMetadataSettingsMapping = {
|
|||||||
[FieldMetadataType.RELATION]: FieldMetadataRelationSettings;
|
[FieldMetadataType.RELATION]: FieldMetadataRelationSettings;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingsByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
|
||||||
T extends keyof FieldMetadataSettingsMapping
|
|
||||||
? FieldMetadataSettingsMapping[T] & FieldMetadataDefaultSettings
|
|
||||||
: T extends 'default'
|
|
||||||
? FieldMetadataDefaultSettings
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type FieldMetadataSettings<
|
export type FieldMetadataSettings<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> = SettingsByFieldMetadata<T>;
|
> =
|
||||||
|
IsExactly<T, FieldMetadataType> extends true
|
||||||
|
? FieldMetadataDefaultSettings
|
||||||
|
: T extends keyof FieldMetadataSettingsMapping
|
||||||
|
? FieldMetadataSettingsMapping[T] & FieldMetadataDefaultSettings
|
||||||
|
: never;
|
||||||
|
|||||||
@ -9,10 +9,10 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
|||||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||||
|
|
||||||
export interface FieldMetadataInterface<
|
export interface FieldMetadataInterface<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> {
|
> {
|
||||||
id: string;
|
id: string;
|
||||||
type: FieldMetadataType;
|
type: T;
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||||
|
|||||||
@ -21,12 +21,12 @@ export function computeColumnName(
|
|||||||
fieldName: string,
|
fieldName: string,
|
||||||
options?: ComputeColumnNameOptions,
|
options?: ComputeColumnNameOptions,
|
||||||
): string;
|
): string;
|
||||||
export function computeColumnName<T extends FieldMetadataType | 'default'>(
|
export function computeColumnName<T extends FieldMetadataType>(
|
||||||
fieldMetadata: FieldMetadataInterface<T>,
|
fieldMetadata: FieldMetadataInterface<T>,
|
||||||
ioptions?: ComputeColumnNameOptions,
|
ioptions?: ComputeColumnNameOptions,
|
||||||
): string;
|
): string;
|
||||||
// TODO: If we need to implement custom name logic for columns, we can do it here
|
// TODO: If we need to implement custom name logic for columns, we can do it here
|
||||||
export function computeColumnName<T extends FieldMetadataType | 'default'>(
|
export function computeColumnName<T extends FieldMetadataType>(
|
||||||
fieldMetadataOrFieldName: FieldMetadataInterface<T> | string,
|
fieldMetadataOrFieldName: FieldMetadataInterface<T> | string,
|
||||||
options?: ComputeColumnNameOptions,
|
options?: ComputeColumnNameOptions,
|
||||||
): string {
|
): string {
|
||||||
@ -51,15 +51,11 @@ export function computeCompositeColumnName(
|
|||||||
fieldName: string,
|
fieldName: string,
|
||||||
compositeProperty: CompositeProperty,
|
compositeProperty: CompositeProperty,
|
||||||
): string;
|
): string;
|
||||||
export function computeCompositeColumnName<
|
export function computeCompositeColumnName<T extends FieldMetadataType>(
|
||||||
T extends FieldMetadataType | 'default',
|
|
||||||
>(
|
|
||||||
fieldMetadata: FieldTypeAndNameMetadata | FieldMetadataInterface<T>,
|
fieldMetadata: FieldTypeAndNameMetadata | FieldMetadataInterface<T>,
|
||||||
compositeProperty: CompositeProperty,
|
compositeProperty: CompositeProperty,
|
||||||
): string;
|
): string;
|
||||||
export function computeCompositeColumnName<
|
export function computeCompositeColumnName<T extends FieldMetadataType>(
|
||||||
T extends FieldMetadataType | 'default',
|
|
||||||
>(
|
|
||||||
fieldMetadataOrFieldName:
|
fieldMetadataOrFieldName:
|
||||||
| FieldTypeAndNameMetadata
|
| FieldTypeAndNameMetadata
|
||||||
| FieldMetadataInterface<T>
|
| FieldMetadataInterface<T>
|
||||||
|
|||||||
@ -67,9 +67,7 @@ export class CreateObjectInput {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Field(() => GraphQLJSON, { nullable: true })
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
primaryKeyFieldMetadataSettings?: FieldMetadataSettings<
|
primaryKeyFieldMetadataSettings?: FieldMetadataSettings<FieldMetadataType>;
|
||||||
FieldMetadataType | 'default'
|
|
||||||
>;
|
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export class ObjectMetadataRelationService {
|
|||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
relationObjectMetadataStandardId: string,
|
relationObjectMetadataStandardId: string,
|
||||||
) {
|
) {
|
||||||
@ -109,7 +109,7 @@ export class ObjectMetadataRelationService {
|
|||||||
relatedObjectMetadata: ObjectMetadataEntity,
|
relatedObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
) {
|
) {
|
||||||
return this.fieldMetadataRepository.save([
|
return this.fieldMetadataRepository.save([
|
||||||
@ -340,7 +340,7 @@ export class ObjectMetadataRelationService {
|
|||||||
relatedObjectMetadata: ObjectMetadataEntity,
|
relatedObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
isUpdate = false,
|
isUpdate = false,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -147,7 +147,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
toObjectMetadata,
|
toObjectMetadata,
|
||||||
[
|
[
|
||||||
foreignKeyFieldMetadata,
|
foreignKeyFieldMetadata,
|
||||||
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
|
deletedAtFieldMetadata as FieldMetadataEntity<FieldMetadataType>,
|
||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@ -451,7 +451,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
relationMetadata.toObjectMetadata,
|
relationMetadata.toObjectMetadata,
|
||||||
[
|
[
|
||||||
foreignKeyFieldMetadata,
|
foreignKeyFieldMetadata,
|
||||||
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
|
deletedAtFieldMetadata as FieldMetadataEntity<FieldMetadataType>,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -570,7 +570,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private throwIfDeletedAtFieldMetadataNotFound(
|
private throwIfDeletedAtFieldMetadataNotFound(
|
||||||
deletedAtFieldMetadata?: FieldMetadataEntity<'default'> | null,
|
deletedAtFieldMetadata?: FieldMetadataEntity<FieldMetadataType> | null,
|
||||||
) {
|
) {
|
||||||
if (!isDefined(deletedAtFieldMetadata)) {
|
if (!isDefined(deletedAtFieldMetadata)) {
|
||||||
throw new RelationMetadataException(
|
throw new RelationMetadataException(
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export class RemoteTableRelationsService {
|
|||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
remoteObjectMetadata: ObjectMetadataEntity,
|
remoteObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
objectPrimaryKeyColumnType?: string,
|
objectPrimaryKeyColumnType?: string,
|
||||||
) {
|
) {
|
||||||
@ -150,7 +150,7 @@ export class RemoteTableRelationsService {
|
|||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
) {
|
) {
|
||||||
const attachmentObjectMetadata =
|
const attachmentObjectMetadata =
|
||||||
@ -190,7 +190,7 @@ export class RemoteTableRelationsService {
|
|||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
) {
|
) {
|
||||||
const timelineActivityObjectMetadata =
|
const timelineActivityObjectMetadata =
|
||||||
@ -230,7 +230,7 @@ export class RemoteTableRelationsService {
|
|||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
objectPrimaryKeyType: FieldMetadataType,
|
objectPrimaryKeyType: FieldMetadataType,
|
||||||
objectPrimaryKeyFieldSettings:
|
objectPrimaryKeyFieldSettings:
|
||||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
| FieldMetadataSettings<FieldMetadataType>
|
||||||
| undefined,
|
| undefined,
|
||||||
) {
|
) {
|
||||||
const favoriteObjectMetadata =
|
const favoriteObjectMetadata =
|
||||||
|
|||||||
@ -37,12 +37,12 @@ export const mapUdtNameToFieldSettings = (
|
|||||||
case 'int4':
|
case 'int4':
|
||||||
return {
|
return {
|
||||||
dataType: NumberDataType.INT,
|
dataType: NumberDataType.INT,
|
||||||
} satisfies FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
} as FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
||||||
case 'int8':
|
case 'int8':
|
||||||
case 'bigint':
|
case 'bigint':
|
||||||
return {
|
return {
|
||||||
dataType: NumberDataType.BIGINT,
|
dataType: NumberDataType.BIGINT,
|
||||||
} satisfies FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
} as FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,9 +18,8 @@ import {
|
|||||||
WorkspaceMigrationExceptionCode,
|
WorkspaceMigrationExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception';
|
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception';
|
||||||
|
|
||||||
export class ColumnActionAbstractFactory<
|
export class ColumnActionAbstractFactory<T extends FieldMetadataType>
|
||||||
T extends FieldMetadataType | 'default',
|
implements WorkspaceColumnActionFactory<T>
|
||||||
> implements WorkspaceColumnActionFactory<T>
|
|
||||||
{
|
{
|
||||||
protected readonly logger = new Logger(ColumnActionAbstractFactory.name);
|
protected readonly logger = new Logger(ColumnActionAbstractFactory.name);
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,7 @@ import {
|
|||||||
WorkspaceMigrationColumnActionType,
|
WorkspaceMigrationColumnActionType,
|
||||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||||
|
|
||||||
export interface WorkspaceColumnActionFactory<
|
export interface WorkspaceColumnActionFactory<T extends FieldMetadataType> {
|
||||||
T extends FieldMetadataType | 'default',
|
|
||||||
> {
|
|
||||||
create(
|
create(
|
||||||
action:
|
action:
|
||||||
| WorkspaceMigrationColumnActionType.CREATE
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
|
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||||
import {
|
import {
|
||||||
FieldMetadataNumberSettings,
|
NumberDataType
|
||||||
FieldMetadataTextSettings,
|
|
||||||
NumberDataType,
|
|
||||||
} from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
} from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||||
import { ObjectMetadataSeed } from 'src/engine/seeder/interfaces/object-metadata-seed';
|
import { ObjectMetadataSeed } from 'src/engine/seeder/interfaces/object-metadata-seed';
|
||||||
|
|
||||||
@ -22,8 +21,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = {
|
|||||||
dataType: NumberDataType.FLOAT,
|
dataType: NumberDataType.FLOAT,
|
||||||
decimals: 3,
|
decimals: 3,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
} as FieldMetadataNumberSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
|
||||||
{
|
{
|
||||||
type: FieldMetadataType.NUMBER,
|
type: FieldMetadataType.NUMBER,
|
||||||
label: 'Percentage of completion (Float 3 decimals + percentage)',
|
label: 'Percentage of completion (Float 3 decimals + percentage)',
|
||||||
@ -32,8 +31,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = {
|
|||||||
dataType: NumberDataType.FLOAT,
|
dataType: NumberDataType.FLOAT,
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
type: 'percentage',
|
type: 'percentage',
|
||||||
} as FieldMetadataNumberSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
|
||||||
{
|
{
|
||||||
type: FieldMetadataType.NUMBER,
|
type: FieldMetadataType.NUMBER,
|
||||||
label: 'Participants (Int)',
|
label: 'Participants (Int)',
|
||||||
@ -41,8 +40,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = {
|
|||||||
settings: {
|
settings: {
|
||||||
dataType: NumberDataType.INT,
|
dataType: NumberDataType.INT,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
} as FieldMetadataNumberSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
|
||||||
{
|
{
|
||||||
type: FieldMetadataType.NUMBER,
|
type: FieldMetadataType.NUMBER,
|
||||||
label: 'Average estimated number of atoms in the universe (BigInt)',
|
label: 'Average estimated number of atoms in the universe (BigInt)',
|
||||||
@ -50,23 +49,23 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = {
|
|||||||
settings: {
|
settings: {
|
||||||
dataType: NumberDataType.BIGINT,
|
dataType: NumberDataType.BIGINT,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
} as FieldMetadataNumberSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.NUMBER>,
|
||||||
{
|
{
|
||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
label: 'Comments (Max 5 rows)',
|
label: 'Comments (Max 5 rows)',
|
||||||
name: 'comments',
|
name: 'comments',
|
||||||
settings: {
|
settings: {
|
||||||
displayedMaxRows: 5,
|
displayedMaxRows: 5,
|
||||||
} as FieldMetadataTextSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.TEXT>,
|
||||||
{
|
{
|
||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
label: 'Short notes (Max 1 row)',
|
label: 'Short notes (Max 1 row)',
|
||||||
name: 'shortNotes',
|
name: 'shortNotes',
|
||||||
settings: {
|
settings: {
|
||||||
displayedMaxRows: 1,
|
displayedMaxRows: 1,
|
||||||
} as FieldMetadataTextSettings,
|
},
|
||||||
},
|
} as FieldMetadataDTO<FieldMetadataType.TEXT>,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args
|
|||||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||||
|
|
||||||
export interface WorkspaceFieldOptions<
|
export interface WorkspaceFieldOptions<
|
||||||
T extends FieldMetadataType | 'default',
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> {
|
> {
|
||||||
standardId: string;
|
standardId: string;
|
||||||
type: T;
|
type: T;
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
|
||||||
|
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export function isFieldMetadataOfType<
|
||||||
|
Field extends FieldMetadataInterface<FieldMetadataType>,
|
||||||
|
Type extends FieldMetadataType,
|
||||||
|
>(
|
||||||
|
fieldMetadata: Field,
|
||||||
|
type: Type,
|
||||||
|
): fieldMetadata is Field & FieldMetadataInterface<Type>;
|
||||||
|
export function isFieldMetadataOfType<
|
||||||
|
Field extends FieldMetadataEntity<FieldMetadataType>,
|
||||||
|
Type extends FieldMetadataType,
|
||||||
|
>(
|
||||||
|
fieldMetadata: Field,
|
||||||
|
type: Type,
|
||||||
|
): fieldMetadata is Field & FieldMetadataEntity<Type>;
|
||||||
|
export function isFieldMetadataOfType<
|
||||||
|
Field extends
|
||||||
|
| FieldMetadataInterface<FieldMetadataType>
|
||||||
|
| FieldMetadataEntity<FieldMetadataType>,
|
||||||
|
Type extends FieldMetadataType,
|
||||||
|
>(fieldMetadata: Field, type: Type): boolean {
|
||||||
|
return fieldMetadata.type === type;
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
|
||||||
|
|
||||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
|
||||||
|
|
||||||
export const isRelationFieldMetadata = (
|
|
||||||
fieldMetadata: FieldMetadataInterface<'default' | FieldMetadataType.RELATION>,
|
|
||||||
): fieldMetadata is FieldMetadataInterface<FieldMetadataType.RELATION> => {
|
|
||||||
return fieldMetadata.type === FieldMetadataType.RELATION;
|
|
||||||
};
|
|
||||||
@ -5,9 +5,9 @@ import { WorkspaceActivationStatus } from 'twenty-shared';
|
|||||||
import { In, Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseCommandOptions,
|
MigrationCommandOptions,
|
||||||
BaseCommandRunner,
|
MigrationCommandRunner,
|
||||||
} from 'src/database/commands/base.command';
|
} from 'src/database/commands/migration-command/migration-command.runner';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service';
|
import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service';
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace-
|
|||||||
name: 'workspace:clean',
|
name: 'workspace:clean',
|
||||||
description: 'Clean suspended workspace',
|
description: 'Clean suspended workspace',
|
||||||
})
|
})
|
||||||
export class CleanSuspendedWorkspacesCommand extends BaseCommandRunner {
|
export class CleanSuspendedWorkspacesCommand extends MigrationCommandRunner {
|
||||||
private workspaceIds: string[] = [];
|
private workspaceIds: string[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -50,9 +50,9 @@ export class CleanSuspendedWorkspacesCommand extends BaseCommandRunner {
|
|||||||
return suspendedWorkspaces.map((workspace) => workspace.id);
|
return suspendedWorkspaces.map((workspace) => workspace.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
override async executeBaseCommand(
|
override async runMigrationCommand(
|
||||||
_passedParams: string[],
|
_passedParams: string[],
|
||||||
options: BaseCommandOptions,
|
options: MigrationCommandOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { dryRun } = options;
|
const { dryRun } = options;
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
import { Command, Option } from 'nest-commander';
|
import { Command, Option } from 'nest-commander';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
import {
|
||||||
|
ActiveWorkspacesMigrationCommandOptions,
|
||||||
|
ActiveWorkspacesMigrationCommandRunner,
|
||||||
|
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
@ -12,18 +15,16 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
|
|||||||
|
|
||||||
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
||||||
|
|
||||||
// TODO: implement dry-run
|
interface RunWorkspaceMigrationsOptions
|
||||||
interface RunWorkspaceMigrationsOptions {
|
extends ActiveWorkspacesMigrationCommandOptions {
|
||||||
dryRun?: boolean;
|
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
workspaceId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'workspace:sync-metadata',
|
name: 'workspace:sync-metadata',
|
||||||
description: 'Sync metadata',
|
description: 'Sync metadata',
|
||||||
})
|
})
|
||||||
export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner {
|
export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesMigrationCommandRunner {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
@InjectRepository(Workspace, 'core')
|
||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
@ -36,7 +37,7 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner
|
|||||||
super(workspaceRepository, twentyORMGlobalManager);
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeActiveWorkspacesCommand(
|
async runMigrationCommandOnActiveWorkspaces(
|
||||||
_passedParam: string[],
|
_passedParam: string[],
|
||||||
options: RunWorkspaceMigrationsOptions,
|
options: RunWorkspaceMigrationsOptions,
|
||||||
workspaceIds: string[],
|
workspaceIds: string[],
|
||||||
@ -133,15 +134,6 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Option({
|
|
||||||
flags: '-d, --dry-run',
|
|
||||||
description: 'Dry run without applying changes',
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
dryRun(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Option({
|
@Option({
|
||||||
flags: '-f, --force',
|
flags: '-f, --force',
|
||||||
description: 'Force migration',
|
description: 'Force migration',
|
||||||
|
|||||||
5
packages/twenty-shared/src/types/IsExactly.ts
Normal file
5
packages/twenty-shared/src/types/IsExactly.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type IsExactly<T, U> = [T] extends [U]
|
||||||
|
? [U] extends [T]
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
: false;
|
||||||
@ -1,2 +1,4 @@
|
|||||||
export * from './ConnectedAccountProvider';
|
export * from './ConnectedAccountProvider';
|
||||||
export * from './FieldMetadataType';
|
export * from './FieldMetadataType';
|
||||||
|
export * from './IsExactly';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user