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:
parent
a4dd5addde
commit
48b45d2773
61
dictation_server/src/common/entity/bigintTransformer.spec.ts
Normal file
61
dictation_server/src/common/entity/bigintTransformer.spec.ts
Normal 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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
57
dictation_server/src/common/entity/index.ts
Normal file
57
dictation_server/src/common/entity/index.ts
Normal 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();
|
||||
},
|
||||
};
|
||||
@ -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' })
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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' })
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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' })
|
||||
|
||||
@ -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' })
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user