Merged PR 518: Entityのtransformer横展開対応

## 概要
[Task2899: 横展開対応](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2899)

- Bigintがコード上でstringとして扱われるのを考慮し、number型に変換する処理を実装
- テスト実装
- 各Entityに展開

## レビューポイント
- 適用漏れはないか
- 実装内容に疑問はないか

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## 動作確認状況
- ローカルでテストが通ることを確認。ローカルで軽く画面を確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2023-10-24 07:54:46 +00:00
parent a4dd5addde
commit 48b45d2773
8 changed files with 142 additions and 18 deletions

View File

@ -0,0 +1,61 @@
import { bigintTransformer } from '.';
describe('bigintTransformer', () => {
describe('to', () => {
it('number型を整数を表す文字列に変換できる', () => {
expect(bigintTransformer.to(0)).toBe('0');
expect(bigintTransformer.to(1)).toBe('1');
expect(bigintTransformer.to(1234567890)).toBe('1234567890');
expect(bigintTransformer.to(9007199254740991)).toBe('9007199254740991');
expect(bigintTransformer.to(-1)).toBe('-1');
});
it('少数点以下がある場合はエラーとなる', () => {
expect(() => bigintTransformer.to(1.1)).toThrowError(
'1.1 is not integer.',
);
});
it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
expect(() => bigintTransformer.to(9007199254740992)).toThrowError(
'value is greater than 9007199254740991.',
);
expect(() => bigintTransformer.to(9223372036854775807)).toThrowError(
'value is greater than 9007199254740991.',
);
});
});
describe('from', () => {
it('bigint型の文字列をnumber型に変換できる', () => {
expect(bigintTransformer.from('0')).toBe(0);
expect(bigintTransformer.from('1')).toBe(1);
expect(bigintTransformer.from('1234567890')).toBe(1234567890);
expect(bigintTransformer.from('-1')).toBe(-1);
});
it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
expect(() => bigintTransformer.from('9007199254740992')).toThrowError(
'9007199254740992 is greater than 9007199254740991.',
);
expect(() => bigintTransformer.from('9223372036854775807')).toThrowError(
'9223372036854775807 is greater than 9007199254740991.',
);
});
it('number型の場合はそのまま返す', () => {
expect(bigintTransformer.from(0)).toBe(0);
expect(bigintTransformer.from(1)).toBe(1);
expect(bigintTransformer.from(1234567890)).toBe(1234567890);
expect(bigintTransformer.from(-1)).toBe(-1);
});
it('nullの場合はそのまま返す', () => {
expect(bigintTransformer.from(null)).toBe(null);
});
it('number型に変換できない場合はエラーとなる', () => {
expect(() => bigintTransformer.from('a')).toThrowError('a is not int.');
expect(() => bigintTransformer.from('')).toThrowError(' is not int.');
expect(() => bigintTransformer.from(undefined)).toThrowError(
'undefined is not string.',
);
expect(() => bigintTransformer.from({})).toThrowError(
'[object Object] is not string.',
);
});
});
});

View File

@ -0,0 +1,57 @@
import { ValueTransformer } from 'typeorm';
// DBのbigint型をnumber型に変換するためのtransformer
// DBのBigInt型をそのまま扱うと、JSのNumber型の最大値を超えると誤差が発生するため、本来はNumber型に変換すべきではないが、
// 影響範囲を最小限に抑えるため、Number型に変換する。使用するのはAutoIncrementされるIDのみの想定のため、
// Number.MAX_SAFE_INTEGERより大きい値は現実的には発生しない想定で変換する。
export const bigintTransformer: ValueTransformer = {
from: (value: any): number | null => {
// valueがnullであればそのまま返す
if (value === null) {
return value;
}
// valueがnumber型かどうかを判定
// 利用DBによってはbigint型であってもnumber型で返ってくる場合があるため、number型の場合はそのまま返す(sqliteの場合)
if (typeof value === 'number') {
return value;
}
// valueが文字列かどうかを判定
if (typeof value !== 'string') {
throw new Error(`${value} is not string.`);
}
// 数値に変換可能な文字列かどうかを判定
if (Number.isNaN(parseInt(value))) {
throw new Error(`${value} is not int.`);
}
// 文字列ならbigintに変換
// valueが整数でない場合は値が丸められてしまうが、TypeORMのEntityの定義上、整数を表す文字列以外はありえないため、少数点は考慮しない
const bigIntValue = BigInt(value);
// bigIntValueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
if (bigIntValue > Number.MAX_SAFE_INTEGER) {
throw new Error(`${value} is greater than ${Number.MAX_SAFE_INTEGER}.`);
}
// number型で表現できる整数であればnumber型に変換して返す
return Number(bigIntValue);
},
to: (value: any): string | null | undefined => {
// valueがnullまたはundefinedであればそのまま返す
if (value === null || value === undefined) {
return value;
}
// valueがnumber型かどうかを判定
if (typeof value !== 'number') {
throw new Error(`${value} is not number.`);
}
// valueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
if (value > Number.MAX_SAFE_INTEGER) {
throw new Error(`value is greater than ${Number.MAX_SAFE_INTEGER}.`);
}
// valueが整数かどうかを判定
if (!Number.isInteger(value)) {
throw new Error(`${value} is not integer.`);
}
return value.toString();
},
};

View File

@ -1,3 +1,4 @@
import { bigintTransformer } from '../../../common/entity';
import { User } from '../../../repositories/users/entity/user.entity';
import {
Entity,
@ -13,7 +14,7 @@ export class Account {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
parent_account_id: number | null;
@Column()
@ -34,13 +35,13 @@ export class Account {
@Column({ default: false })
verified: boolean;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
primary_admin_user_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
secondary_admin_user_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
active_worktype_id: number | null;
@Column({ nullable: true, type: 'datetime' })

View File

@ -1,3 +1,4 @@
import { bigintTransformer } from '../../../common/entity';
import { Task } from '../../../repositories/tasks/entity/task.entity';
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
import { User } from '../../../repositories/users/entity/user.entity';
@ -18,10 +19,10 @@ export class CheckoutPermission {
@Column({})
task_id: number;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
user_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
user_group_id: number | null;
@OneToOne(() => User, (user) => user.id)

View File

@ -10,6 +10,7 @@ import {
PrimaryColumn,
} from 'typeorm';
import { User } from '../../users/entity/user.entity';
import { bigintTransformer } from '../../../common/entity';
@Entity({ name: 'license_orders' })
export class LicenseOrder {
@ -79,16 +80,16 @@ export class License {
@Column()
status: string;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
allocated_user_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
order_id: number | null;
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
delete_order_id: number | null;
@Column({ nullable: true, type: 'datetime' })
@ -244,16 +245,16 @@ export class LicenseArchive {
@Column()
status: string;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
allocated_user_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
order_id: number | null;
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
delete_order_id: number | null;
@Column({ nullable: true, type: 'datetime' })

View File

@ -11,6 +11,7 @@ import {
OneToMany,
ManyToOne,
} from 'typeorm';
import { bigintTransformer } from '../../../common/entity';
@Entity({ name: 'tasks' })
export class Task {
@ -26,11 +27,11 @@ export class Task {
audio_file_id: number;
@Column()
status: string;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
typist_user_id: number | null;
@Column()
priority: string;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
template_file_id: number | null;
@Column({ nullable: true, type: 'datetime' })
started_at: Date | null;

View File

@ -12,6 +12,7 @@ import { WorkflowTypist } from './workflow_typists.entity';
import { Worktype } from '../../worktypes/entity/worktype.entity';
import { TemplateFile } from '../../template_files/entity/template_file.entity';
import { User } from '../../users/entity/user.entity';
import { bigintTransformer } from '../../../common/entity';
@Entity({ name: 'workflows' })
export class Workflow {
@ -24,10 +25,10 @@ export class Workflow {
@Column()
author_id: number;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
worktype_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
template_id: number | null;
@Column({ nullable: true, type: 'datetime' })

View File

@ -10,6 +10,7 @@ import {
import { Workflow } from './workflow.entity';
import { User } from '../../users/entity/user.entity';
import { UserGroup } from '../../user_groups/entity/user_group.entity';
import { bigintTransformer } from '../../../common/entity';
@Entity({ name: 'workflow_typists' })
export class WorkflowTypist {
@ -19,10 +20,10 @@ export class WorkflowTypist {
@Column()
workflow_id: number;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
typist_id: number | null;
@Column({ nullable: true, type: 'int' })
@Column({ nullable: true, type: 'bigint', transformer: bigintTransformer })
typist_group_id: number | null;
@Column({ nullable: true, type: 'datetime' })