Timestamp column migration in all workspace schema tables (#13679)
fixes https://github.com/twentyhq/private-issues/issues/288 Test with : `npx nx run twenty-server:command upgrade:1-3:update-timestamp-column-type-in-workspace-schema`
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
RunOnWorkspaceArgs,
|
||||
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
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 { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-3:update-timestamp-column-type-in-workspace-schema',
|
||||
description:
|
||||
'Update the timestamp column type in all the workspace schema tables',
|
||||
})
|
||||
export class UpdateTimestampColumnTypeInWorkspaceSchemaCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectRepository(FieldMetadataEntity, 'core')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const dateTimeFieldMetadataItems = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
},
|
||||
relations: ['object'],
|
||||
});
|
||||
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
const schemaName = getWorkspaceSchemaName(workspaceId);
|
||||
|
||||
for (const fieldMetadataItem of dateTimeFieldMetadataItems) {
|
||||
this.logger.log(
|
||||
`Updating column type for ${fieldMetadataItem.name} in ${schemaName}."${computeObjectTargetTable(fieldMetadataItem.object)}"`,
|
||||
);
|
||||
await mainDataSource.query(
|
||||
`ALTER TABLE ${schemaName}."${computeObjectTargetTable(fieldMetadataItem.object)}"
|
||||
ALTER COLUMN "${fieldMetadataItem.name}" TYPE timestamptz(3);`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command';
|
||||
import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command';
|
||||
import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command';
|
||||
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
|
||||
import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
@@ -41,10 +42,12 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
|
||||
providers: [
|
||||
AssignRolesToExistingApiKeysCommand,
|
||||
AddNextStepIdsToWorkflowRunsTrigger,
|
||||
UpdateTimestampColumnTypeInWorkspaceSchemaCommand,
|
||||
],
|
||||
exports: [
|
||||
AssignRolesToExistingApiKeysCommand,
|
||||
AddNextStepIdsToWorkflowRunsTrigger,
|
||||
UpdateTimestampColumnTypeInWorkspaceSchemaCommand,
|
||||
],
|
||||
})
|
||||
export class V1_3_UpgradeVersionCommandModule {}
|
||||
|
||||
+3
-1
@@ -30,6 +30,7 @@ import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/u
|
||||
import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command';
|
||||
import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command';
|
||||
import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command';
|
||||
import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
@@ -158,8 +159,8 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
|
||||
// 1.3 Commands
|
||||
protected readonly assignRolesToExistingApiKeysCommand: AssignRolesToExistingApiKeysCommand,
|
||||
// protected readonly addNextStepIdsToWorkflowVersionTriggers: AddNextStepIdsToWorkflowVersionTriggers,
|
||||
protected readonly addNextStepIdsToWorkflowRunsTrigger: AddNextStepIdsToWorkflowRunsTrigger,
|
||||
protected readonly updateTimestampColumnTypeInWorkspaceSchemaCommand: UpdateTimestampColumnTypeInWorkspaceSchemaCommand,
|
||||
) {
|
||||
super(
|
||||
workspaceRepository,
|
||||
@@ -223,6 +224,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
this.addNextStepIdsToWorkflowVersionTriggers, // We add that command again because nextStepIds where not added on freshly created triggers. It will be done in 1.3
|
||||
this.addNextStepIdsToWorkflowRunsTrigger,
|
||||
this.assignRolesToExistingApiKeysCommand,
|
||||
this.updateTimestampColumnTypeInWorkspaceSchemaCommand,
|
||||
],
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { getFieldMetadataType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-field-metadata-type.util';
|
||||
|
||||
describe('getFieldMetadataType', () => {
|
||||
it.each([
|
||||
['uuid', FieldMetadataType.UUID],
|
||||
['timestamptz', FieldMetadataType.DATE_TIME],
|
||||
])(
|
||||
'should return correct FieldMetadataType for type %s',
|
||||
(type, expectedMetadataType) => {
|
||||
expect(getFieldMetadataType(type)).toBe(expectedMetadataType);
|
||||
},
|
||||
);
|
||||
|
||||
it('should throw an error for an unknown type', () => {
|
||||
const unknownType = 'unknownType';
|
||||
|
||||
expect(() => getFieldMetadataType(unknownType)).toThrow(
|
||||
`Unknown type ${unknownType}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
const typeOrmTypeMapping = new Map<string, FieldMetadataType>([
|
||||
['uuid', FieldMetadataType.UUID],
|
||||
['timestamptz', FieldMetadataType.DATE_TIME],
|
||||
// Add more types here if we need to support more than id, and createdAt/updatedAt/deletedAt
|
||||
]);
|
||||
|
||||
export const getFieldMetadataType = (type: string) => {
|
||||
const fieldType = typeOrmTypeMapping.get(type);
|
||||
|
||||
if (fieldType === undefined || fieldType === null) {
|
||||
throw new Error(`Unknown type ${type}`);
|
||||
}
|
||||
|
||||
return fieldType;
|
||||
};
|
||||
@@ -90,6 +90,8 @@ export class EntitySchemaColumnFactory {
|
||||
entitySchemaColumnMap[key] = {
|
||||
name: key,
|
||||
type: columnType as ColumnType,
|
||||
precision:
|
||||
fieldMetadata.type === FieldMetadataType.DATE_TIME ? 3 : undefined,
|
||||
// TODO: We should double check that
|
||||
primary: key === 'id',
|
||||
nullable: fieldMetadata.isNullable ?? false,
|
||||
|
||||
+4
@@ -179,6 +179,10 @@ export class WorkspaceMigrationColumnService {
|
||||
column: {
|
||||
name: createColumnMigration.columnName,
|
||||
type: createColumnMigration.columnType,
|
||||
precision:
|
||||
createColumnMigration.columnType === 'timestamptz'
|
||||
? 3
|
||||
: undefined,
|
||||
isArray: createColumnMigration.isArray ?? false,
|
||||
isNullable: createColumnMigration.isNullable,
|
||||
default: createColumnMigration.defaultValue,
|
||||
|
||||
+5
-2
@@ -15,13 +15,16 @@ export class WorkspaceMigrationTypeService {
|
||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||
) {
|
||||
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||
const computedColumnType = ` ${columnDefinition.columnType}${
|
||||
columnDefinition.columnType === 'timestamptz' ? `(3)` : ''
|
||||
}`;
|
||||
|
||||
// Update the column type
|
||||
// If casting is not possible, the query will fail
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "${schemaName}"."${tableName}"
|
||||
ALTER COLUMN "${columnDefinition.columnName}" TYPE ${columnDefinition.columnType}
|
||||
USING "${columnDefinition.columnName}"::${columnDefinition.columnType}
|
||||
ALTER COLUMN "${columnDefinition.columnName}" TYPE ${computedColumnType}
|
||||
USING "${columnDefinition.columnName}"::${computedColumnType}
|
||||
`);
|
||||
|
||||
// Update the column default value
|
||||
|
||||
+2
@@ -19,6 +19,7 @@ export const typeormBuildCreateColumnSql = ({
|
||||
| 'default'
|
||||
| 'generatedType'
|
||||
| 'asExpression'
|
||||
| 'precision'
|
||||
>;
|
||||
}): string => {
|
||||
let columnSql = '"' + column.name + '"';
|
||||
@@ -30,6 +31,7 @@ export const typeormBuildCreateColumnSql = ({
|
||||
})}"`;
|
||||
} else {
|
||||
columnSql += ' ' + column.type;
|
||||
if (column.precision) columnSql += `(${column.precision})`;
|
||||
}
|
||||
|
||||
if (column.isArray) columnSql += '[]';
|
||||
|
||||
Reference in New Issue
Block a user