Switch timestamp to timestamptz (#4696)

* Switch timestamps to timestamptz

* update standard/custom objects logic to use timestamptz

* fix test
This commit is contained in:
Weiko
2024-03-28 22:39:41 +01:00
committed by GitHub
parent 27fdb00d07
commit 1829f4d009
21 changed files with 209 additions and 79 deletions

View File

@ -0,0 +1,71 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UseTimestampWithTZ1711633823798 implements MigrationInterface {
name = 'UseTimestampWithTZ1711633823798';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."featureFlag" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."featureFlag" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."workspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."user" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."user" ALTER COLUMN "passwordResetTokenExpiresAt" TYPE TIMESTAMP WITH TIME ZONE USING "passwordResetTokenExpiresAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt" AT TIME ZONE 'UTC'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."featureFlag" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."featureFlag" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."billingSubscriptionItem" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."billingSubscription" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."workspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."user" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."user" ALTER COLUMN "passwordResetTokenExpiresAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`,
);
}
}

View File

@ -0,0 +1,95 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UseTimestampWithTZ1711619086385 implements MigrationInterface {
name = 'UseTimestampWithTZ1711619086385';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceMigration" ALTER COLUMN "appliedAt" TYPE TIMESTAMP WITH TIME ZONE USING "appliedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceMigration" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceCacheVersion" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceCacheVersion" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."remoteServer" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."remoteServer" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."dataSource" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."dataSource" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."fieldMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."fieldMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."relationMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt" AT TIME ZONE 'UTC'`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."relationMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt" AT TIME ZONE 'UTC'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceMigration" ALTER COLUMN "appliedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceMigration" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceCacheVersion" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."workspaceCacheVersion" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."remoteServer" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."remoteServer" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."dataSource" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."dataSource" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."fieldMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."fieldMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."relationMetadata" ALTER COLUMN "createdAt" TYPE TIMESTAMP`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."relationMetadata" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`,
);
}
}

View File

@ -4,7 +4,7 @@ import { getFieldMetadataType } from 'src/engine/api/graphql/workspace-schema-bu
describe('getFieldMetadataType', () => {
it.each([
['uuid', FieldMetadataType.UUID],
['timestamp', FieldMetadataType.DATE_TIME],
['timestamptz', FieldMetadataType.DATE_TIME],
])(
'should return correct FieldMetadataType for type %s',
(type, expectedMetadataType) => {

View File

@ -2,7 +2,7 @@ import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/fi
const typeOrmTypeMapping = new Map<string, FieldMetadataType>([
['uuid', FieldMetadataType.UUID],
['timestamp', FieldMetadataType.DATE_TIME],
['timestamptz', FieldMetadataType.DATE_TIME],
// Add more types here if we need to support more than id, and createdAt/updatedAt/deletedAt
]);

View File

@ -52,20 +52,20 @@ export class AppToken {
value: string;
@Field()
@Column('timestamp with time zone')
@Column({ type: 'timestamptz' })
expiresAt: Date;
@Column('timestamp with time zone', { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt: Date | null;
@Column('timestamp with time zone', { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
revokedAt: Date | null;
@Field()
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@Field()
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -23,13 +23,13 @@ export class BillingSubscriptionItem {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt?: Date;
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@Column({ nullable: false })

View File

@ -23,13 +23,13 @@ export class BillingSubscription {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt?: Date;
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@ManyToOne(() => Workspace, (workspace) => workspace.billingSubscriptions, {

View File

@ -47,9 +47,9 @@ export class FeatureFlagEntity {
@Column({ nullable: false })
value: boolean;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -46,14 +46,14 @@ export class UserWorkspace {
workspaceId: string;
@Field()
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@Field()
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@Field({ nullable: true })
@Column('timestamp with time zone', { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt: Date;
}

View File

@ -56,15 +56,15 @@ export class User {
canImpersonate: boolean;
@Field()
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@Field()
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@Field({ nullable: true })
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt: Date;
@Field(() => Workspace, { nullable: false })
@ -82,7 +82,7 @@ export class User {
passwordResetToken: string;
@Field({ nullable: true })
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
passwordResetTokenExpiresAt: Date;
@OneToMany(() => AppToken, (appToken) => appToken.user, {

View File

@ -45,15 +45,15 @@ export class Workspace {
inviteHash?: string;
@Field({ nullable: true })
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
deletedAt?: Date;
@Field()
@CreateDateColumn({ type: 'timestamp with time zone' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@Field()
@UpdateDateColumn({ type: 'timestamp with time zone' })
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@OneToMany(() => AppToken, (appToken) => appToken.workspace, {

View File

@ -40,9 +40,9 @@ export class DataSourceEntity {
@Column({ nullable: false, type: 'uuid' })
workspaceId: string;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -117,9 +117,9 @@ export class FieldMetadataEntity<
)
toRelationMetadata: RelationMetadataEntity;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -101,9 +101,9 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
})
dataSource: DataSourceEntity;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -90,9 +90,9 @@ export class RelationMetadataEntity implements RelationMetadataInterface {
@JoinColumn()
toFieldMetadata: FieldMetadataEntity;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -51,9 +51,9 @@ export class RemoteServerEntity<T extends RemoteServerType> {
@Column({ nullable: false, type: 'uuid' })
workspaceId: string;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -17,9 +17,9 @@ export class WorkspaceCacheVersionEntity {
@Column()
version: string;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -24,7 +24,7 @@ export const fieldMetadataTypeToColumnType = <Type extends FieldMetadataType>(
case FieldMetadataType.BOOLEAN:
return 'boolean';
case FieldMetadataType.DATE_TIME:
return 'timestamp';
return 'timestamptz';
case FieldMetadataType.RATING:
case FieldMetadataType.SELECT:
case FieldMetadataType.MULTI_SELECT:

View File

@ -86,12 +86,12 @@ export class WorkspaceMigrationEntity {
@Column({ default: false })
isCustom: boolean;
@Column({ nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
appliedAt?: Date;
@Column({ nullable: false, type: 'uuid' })
workspaceId: string;
@CreateDateColumn()
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
}

View File

@ -1,36 +0,0 @@
import { ConflictException } from '@nestjs/common';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
export const mapFieldMetadataTypeToDataType = (
fieldMetadataType: FieldMetadataType,
): string => {
switch (fieldMetadataType) {
case FieldMetadataType.UUID:
return 'uuid';
case FieldMetadataType.TEXT:
return 'text';
case FieldMetadataType.PHONE:
case FieldMetadataType.EMAIL:
return 'varchar';
case FieldMetadataType.NUMERIC:
return 'numeric';
case FieldMetadataType.NUMBER:
case FieldMetadataType.PROBABILITY:
return 'double precision';
case FieldMetadataType.BOOLEAN:
return 'boolean';
case FieldMetadataType.DATE_TIME:
return 'timestamp';
case FieldMetadataType.RAW_JSON:
return 'jsonb';
case FieldMetadataType.RATING:
case FieldMetadataType.SELECT:
case FieldMetadataType.MULTI_SELECT:
return 'enum';
default:
throw new ConflictException(
`Cannot convert ${fieldMetadataType} to data type.`,
);
}
};

View File

@ -9,17 +9,17 @@ export const customTableDefaultColumns: TableColumnOptions[] = [
},
{
name: 'createdAt',
type: 'timestamp',
type: 'timestamptz',
default: 'now()',
},
{
name: 'updatedAt',
type: 'timestamp',
type: 'timestamptz',
default: 'now()',
},
{
name: 'deletedAt',
type: 'timestamp',
type: 'timestamptz',
isNullable: true,
},
];