pg-mvc-service 2.0.43 → 2.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/models/TableDoc.js +1 -1
  2. package/index.d.ts +189 -0
  3. package/package.json +1 -5
  4. package/src/PoolManager.ts +48 -0
  5. package/src/Service.ts +276 -0
  6. package/src/Utils/DateTimeUtil.ts +146 -0
  7. package/src/Utils/NumberUtil.ts +23 -0
  8. package/src/Utils/StringUtil.ts +33 -0
  9. package/src/assets/favicon.ico +0 -0
  10. package/src/clients/AwsS3Client.ts +310 -0
  11. package/src/clients/Base64Client.ts +305 -0
  12. package/src/clients/EncryptClient.ts +100 -0
  13. package/src/clients/StringClient.ts +19 -0
  14. package/src/cron/BaseCron.ts +122 -0
  15. package/src/cron/CronExecuter.ts +34 -0
  16. package/src/cron/CronType.ts +25 -0
  17. package/src/documents/Swagger.ts +105 -0
  18. package/src/exceptions/Exception.ts +72 -0
  19. package/src/index.ts +23 -0
  20. package/src/models/ExpressionClient.ts +72 -0
  21. package/src/models/MigrateDatabase.ts +135 -0
  22. package/src/models/MigrateRollback.ts +151 -0
  23. package/src/models/MigrateTable.ts +56 -0
  24. package/src/models/SqlUtils/SelectExpression.ts +97 -0
  25. package/src/models/SqlUtils/UpdateExpression.ts +29 -0
  26. package/src/models/SqlUtils/ValidateValueUtil.ts +354 -0
  27. package/src/models/SqlUtils/WhereExpression.ts +421 -0
  28. package/src/models/TableDoc.ts +369 -0
  29. package/src/models/TableModel.ts +701 -0
  30. package/src/models/Type.ts +62 -0
  31. package/src/models/Utils/MessageUtil.ts +60 -0
  32. package/src/models/ValidateClient.ts +182 -0
  33. package/src/reqestResponse/ReqResType.ts +170 -0
  34. package/src/reqestResponse/RequestType.ts +918 -0
  35. package/src/reqestResponse/ResponseType.ts +420 -0
  36. package/tsconfig.json +14 -0
@@ -0,0 +1,100 @@
1
+ import crypto from 'crypto';
2
+
3
+ export class EncryptClient {
4
+ private secretKeyHex?: string;
5
+ get SecretKey(): Buffer<ArrayBuffer> {
6
+ if (this.secretKeyHex === undefined) {
7
+ throw new Error("Please set the secret key.");
8
+ }
9
+ return Buffer.from(this.secretKeyHex, 'hex');
10
+ }
11
+ private hmacKeyBase64?: string;
12
+ get HmacKey(): Buffer<ArrayBuffer> {
13
+ if (this.hmacKeyBase64 === undefined) {
14
+ throw new Error("Please set the hmac key.");
15
+ }
16
+ return Buffer.from(this.hmacKeyBase64, 'base64');
17
+ }
18
+
19
+ constructor(params: {secretKeyHex?: string, hmacKeyBase64?: string}) {
20
+ this.secretKeyHex = params?.secretKeyHex;
21
+ this.hmacKeyBase64 = params?.hmacKeyBase64;
22
+ }
23
+
24
+ public encryptAndSign(data: string | {[key: string]: any}): string {
25
+ const iv = crypto.randomBytes(12);
26
+ const cipher = crypto.createCipheriv('aes-256-gcm', this.SecretKey, iv);
27
+
28
+ const plaintext = typeof data === 'string' ? data : JSON.stringify(data);
29
+ const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
30
+ const authTag = cipher.getAuthTag();
31
+
32
+ const combined = Buffer.concat([iv, authTag, encrypted]);
33
+ const payload = this.base64urlEncode(combined);
34
+
35
+ const hmac = crypto.createHmac('sha256', this.HmacKey).update(payload).digest();
36
+ const signature = this.base64urlEncode(hmac);
37
+
38
+ return `${payload}.${signature}`;
39
+ }
40
+
41
+ public isValidToken(token: string): boolean {
42
+ // 形式チェック、.で区切られているか?
43
+ const [payload, signature] = token.split('.');
44
+ if (!payload || !signature) {
45
+ return false;
46
+ }
47
+
48
+ // 改竄チェック
49
+ const expectedSig = crypto.createHmac('sha256', this.HmacKey).update(payload).digest();
50
+ const expectedSigStr = this.base64urlEncode(expectedSig);
51
+
52
+ if (signature !== expectedSigStr) {
53
+ return false;
54
+ }
55
+
56
+ return true;
57
+ }
58
+
59
+ public decrypt<T>(token: string): T {
60
+ // 形式チェック、.で区切られているか?
61
+ const [payload, signature] = token.split('.');
62
+ if (!payload || !signature) {
63
+ throw new Error('Invalid token format');
64
+ }
65
+
66
+ // 改竄チェック
67
+ const expectedSig = crypto.createHmac('sha256', this.HmacKey).update(payload).digest();
68
+ const expectedSigStr = this.base64urlEncode(expectedSig);
69
+ if (signature !== expectedSigStr){
70
+ throw new Error('The token appears to have been tampered with');
71
+ }
72
+
73
+ const combined = this.base64urlDecode(payload);
74
+ const iv = combined.subarray(0, 12);
75
+ const authTag = combined.subarray(12, 28);
76
+ const encrypted = combined.subarray(28);
77
+
78
+ const decipher = crypto.createDecipheriv('aes-256-gcm', this.SecretKey, iv);
79
+ decipher.setAuthTag(authTag);
80
+
81
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
82
+ return JSON.parse(decrypted.toString('utf8')) as T;
83
+ }
84
+
85
+
86
+ private base64urlEncode(buffer: Buffer): string {
87
+ return buffer.toString('base64')
88
+ .replace(/\+/g, '-')
89
+ .replace(/\//g, '_')
90
+ .replace(/=+$/, '');
91
+ }
92
+
93
+ private base64urlDecode(str: string): Buffer {
94
+ str = str.replace(/-/g, '+').replace(/_/g, '/');
95
+ while (str.length % 4 !== 0) {
96
+ str += '=';
97
+ }
98
+ return Buffer.from(str, 'base64');
99
+ }
100
+ }
@@ -0,0 +1,19 @@
1
+ import { randomBytes } from 'crypto';
2
+ import StringUtil from '../Utils/StringUtil';
3
+
4
+ export class StringClient {
5
+ constructor() { }
6
+
7
+ public generateUUIDv7(): string {
8
+ const timestamp = BigInt(Date.now()) * BigInt(10000) + BigInt(process.hrtime.bigint() % BigInt(10000));
9
+ const timeHex = timestamp.toString(16).padStart(16, '0');
10
+
11
+ const randomHex = randomBytes(8).toString('hex');
12
+
13
+ return `${timeHex.slice(0, 8)}-${timeHex.slice(8, 12)}-7${timeHex.slice(13, 16)}-${randomHex.slice(0, 4)}-${randomHex.slice(4)}`;
14
+ }
15
+
16
+ public isUUID(value: any) {
17
+ return StringUtil.isUUID(value);
18
+ }
19
+ }
@@ -0,0 +1,122 @@
1
+ import { Pool, PoolClient } from "pg";
2
+ import { DateType, DayType, HourType, MinuteSecondType, MonthType } from "./CronType";
3
+ import PoolManager from "../PoolManager";
4
+ import { AwsS3Client } from "../clients/AwsS3Client";
5
+ import { Base64Client } from "../clients/Base64Client";
6
+
7
+ export class BaseCron {
8
+
9
+ protected readonly isTest: boolean = process.env.NODE_ENV === 'test';
10
+ protected dbUser?: string = this.isTest ? process.env.TEST_DB_USER : process.env.DB_USER;
11
+ protected dbHost?: string = this.isTest ? process.env.TEST_DB_HOST : process.env.DB_HOST;
12
+ protected dbName?: string = this.isTest ? process.env.TEST_DB_DATABASE : process.env.DB_DATABASE;
13
+ protected dbPassword?: string = this.isTest ? process.env.TEST_DB_PASSWORD : process.env.DB_PASSWORD;
14
+ protected dbPort?: string | number = this.isTest ? process.env.TEST_DB_PORT : process.env.DB_PORT;
15
+ protected dbIsSslConnect: boolean = (this.isTest ? process.env.TEST_DB_IS_SSL : process.env.DB_IS_SSL) === 'true';
16
+
17
+ private isExecuteRollback: boolean = false;
18
+ private pool?: Pool;
19
+ private client?: PoolClient;
20
+ protected get Client(): PoolClient {
21
+ if (this.client === undefined) {
22
+ throw new Error("Please call this.PoolClient after using the setClient method.");
23
+ }
24
+ return this.client;
25
+ }
26
+
27
+ protected async commit(): Promise<void> {
28
+ await this.Client.query('COMMIT');
29
+ this.isExecuteRollback = false;
30
+ }
31
+
32
+ protected async rollback(): Promise<void> {
33
+ if (this.isExecuteRollback) {
34
+ await this.Client.query('ROLLBACK');
35
+ }
36
+ this.isExecuteRollback = false;
37
+ }
38
+
39
+ // **********************************************************************
40
+ // こちらのメソッド、プロパティを各サブクラスで設定してください
41
+ // **********************************************************************
42
+ protected cronCode: string = '';
43
+ protected minute: MinuteSecondType | '*' = '*';
44
+ protected hour: HourType | '*' = '*';
45
+ protected date: DateType | '*' = '*';
46
+ protected month: MonthType | '*' = '*';
47
+ protected day: DayType | '*' = '*';
48
+ public async run() { }
49
+
50
+ // **********************************************************************
51
+ // ベースクラスで設定
52
+ // **********************************************************************
53
+ get CronSchedule(): string {
54
+ let schedule = '';
55
+ schedule += this.minute.toString() + ' ';
56
+ schedule += this.hour.toString() + ' ';
57
+ schedule += this.date.toString() + ' ';
58
+ schedule += this.month.toString() + ' ';
59
+ schedule += this.day.toString();
60
+ return schedule;
61
+ }
62
+ get CronCode(): string { return this.cronCode; }
63
+
64
+ public async setUp() {
65
+ if (this.dbUser === undefined) {
66
+ throw new Error("Database user is not configured");
67
+ }
68
+ if (this.dbHost === undefined) {
69
+ throw new Error("Database host is not configured");
70
+ }
71
+ if (this.dbName === undefined) {
72
+ throw new Error("Database name is not configured");
73
+ }
74
+ if (this.dbPassword === undefined) {
75
+ throw new Error("Database password is not configured");
76
+ }
77
+ if (this.dbPort === undefined) {
78
+ throw new Error("Database port is not configured");
79
+ }
80
+
81
+ this.pool = PoolManager.getPool(this.dbUser, this.dbHost, this.dbName, this.dbPassword, this.dbPort, this.dbIsSslConnect);
82
+ this.pool.query(`SET TIME ZONE '${process.env.TZ ?? 'Asia/Tokyo'}';`);
83
+ this.client = await this.pool.connect();
84
+ await this.Client.query('BEGIN');
85
+ this.isExecuteRollback = true;
86
+ }
87
+
88
+ public async tearDown() {
89
+ try {
90
+ if (this.isExecuteRollback === false) {
91
+ await this.rollback();
92
+ }
93
+ } finally {
94
+ // クライアント接続をリリース
95
+ if (this.client) {
96
+ this.client.release();
97
+ this.client = undefined;
98
+ }
99
+ }
100
+ }
101
+
102
+ private s3Client?: AwsS3Client;
103
+ get S3Client(): AwsS3Client {
104
+ if (this.s3Client === undefined) {
105
+ this.s3Client = new AwsS3Client({
106
+ bucketName: process.env.S3_BUCKET_NAME,
107
+ region: process.env.S3_REGION,
108
+ accessKeyId: process.env.S3_ACCESS_KEY_ID,
109
+ secretAccessKey: process.env.S3_SECRET_ACCESS_KEY
110
+ });
111
+ }
112
+ return this.s3Client;
113
+ }
114
+
115
+ private base64Client? : Base64Client;
116
+ get Base64Client(): Base64Client {
117
+ if (this.base64Client === undefined) {
118
+ this.base64Client = new Base64Client();
119
+ }
120
+ return this.base64Client;
121
+ }
122
+ }
@@ -0,0 +1,34 @@
1
+ const cron = require('node-cron');
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { BaseCron } from "./BaseCron";
5
+
6
+ export const runCron = async (dir: string, logCallback?: (ex: Error) => Promise<void>) => {
7
+ const files = fs.readdirSync(dir);
8
+ for (let file of files) {
9
+ if (['BaseCron.ts'].includes(file)) {
10
+ continue;
11
+ }
12
+
13
+ const filePath = path.join(dir, file);
14
+ const module = await import(filePath);
15
+ const cronClass = module.default;
16
+ const cronInstance: BaseCron = new cronClass();
17
+
18
+ cron.schedule(cronInstance.CronSchedule, async () => {
19
+ try {
20
+ await cronInstance.setUp();
21
+ await cronInstance.run();
22
+ } catch (ex: unknown) {
23
+ if (logCallback !== undefined && ex instanceof Error) {
24
+ await logCallback(ex).
25
+ catch(() => {
26
+ // ログ送信時にエラーになっても握りつぶす
27
+ });
28
+ }
29
+ } finally {
30
+ await cronInstance.tearDown();
31
+ }
32
+ });
33
+ }
34
+ }
@@ -0,0 +1,25 @@
1
+ export type DayType =
2
+ 1 | 2 | 3 | 4 | 5 | 6 | 7
3
+
4
+ export type MonthType =
5
+ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6
+ 10 | 11 | 12
7
+
8
+ export type DateType =
9
+ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10
+ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
11
+ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
12
+ 30 | 31
13
+
14
+ export type HourType =
15
+ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
16
+ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
17
+ 20 | 21 | 22 | 23
18
+
19
+ export type MinuteSecondType =
20
+ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
21
+ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
22
+ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
23
+ 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
24
+ 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
25
+ 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
@@ -0,0 +1,105 @@
1
+ import { Service } from '../Service';
2
+
3
+
4
+ export interface IParams {
5
+ in: 'header' | 'path',
6
+ name: string,
7
+ require?: boolean,
8
+ description?: string,
9
+ example?: string
10
+ }
11
+
12
+ export const createSwagger = (services: Service[], name: string, url: string, params: Array<IParams> = []): string => {
13
+ // *****************************************
14
+ // Internal method definitions
15
+ // *****************************************
16
+ function setYmlByMethod(method: string, swaggerYmlObj: {[key: string]: string}) {
17
+ if (method in swaggerYmlObj) {
18
+ swaggerInfo += ` ${method.toLowerCase()}:\n`;
19
+ swaggerInfo += swaggerYmlObj[method];
20
+ }
21
+ }
22
+ // *****************************************
23
+ // Execution part
24
+ // *****************************************
25
+ const endpontSwaggerYml: {[keyEndpoint: string]: {[keyMethod: string]: string}} = {};
26
+ let tags: Array<string> = [];
27
+
28
+ for (const service of services) {
29
+ if (service.Endpoint in endpontSwaggerYml === false) {
30
+ endpontSwaggerYml[service.Endpoint] = {};
31
+ }
32
+ let yml = "";
33
+
34
+ const apiTags = service.Tags;
35
+ if (apiTags.length > 0) {
36
+ tags = [ ...tags, ...apiTags];
37
+ yml += ` tags:\n`;
38
+ for (const tag of apiTags) {
39
+ yml += ` - ${tag}\n`;
40
+ }
41
+ }
42
+ yml += ` summary: ${service.Summary}\n`;
43
+
44
+ const croneParams = [...params];
45
+ for (const path of service.Endpoint.split('/')) {
46
+ if (path.startsWith('{') && path.endsWith('}')) {
47
+ const key = path.replace('{', '').replace('}', '');
48
+ croneParams.push({
49
+ in: 'path',
50
+ name: key,
51
+ require: true,
52
+ description: key,
53
+ });
54
+ }
55
+ }
56
+
57
+ if (croneParams.length > 0) {
58
+ yml += ` parameters:\n`;
59
+ for (const param of croneParams) {
60
+ yml += ` - in: ${param.in}\n`;
61
+ yml += ` name: ${param.name}\n`;
62
+ yml += ` required: ${param.require === true ? 'true' : 'false'}\n`;
63
+ if (param.description !== undefined) {
64
+ yml += ` description: |\n ${param.description}\n`;
65
+ }
66
+ if (param.example !== undefined) {
67
+ yml += ` example: ${param.example}\n`;
68
+ }
69
+ yml += ` schema:\n`;
70
+ yml += ` type: string\n`;
71
+ }
72
+ }
73
+
74
+ yml += service.Request.createSwagger(service.Method);
75
+ yml += service.Response.createSwagger();
76
+
77
+ endpontSwaggerYml[service.Endpoint][service.Method] = yml;
78
+ }
79
+
80
+ let swaggerInfo = `openapi: 3.0.0
81
+ info:
82
+ title: Your API Documentation
83
+ version: 1.0.0
84
+ description: API documentation for your service
85
+ servers:
86
+ - url: ${url}
87
+ description: ${name} API IF定義書
88
+ tags:\n`;
89
+ for (const tag of tags) {
90
+ swaggerInfo += ` - name: ${tag}\n`;
91
+ }
92
+ swaggerInfo += 'paths:\n'
93
+
94
+ for (const keyEndpoint in endpontSwaggerYml) {
95
+ swaggerInfo += ` ${keyEndpoint}:\n`;
96
+
97
+ setYmlByMethod('GET', endpontSwaggerYml[keyEndpoint]);
98
+ setYmlByMethod('POST', endpontSwaggerYml[keyEndpoint]);
99
+ setYmlByMethod('PUT', endpontSwaggerYml[keyEndpoint]);
100
+ setYmlByMethod('PATCH', endpontSwaggerYml[keyEndpoint]);
101
+ setYmlByMethod('DELETE', endpontSwaggerYml[keyEndpoint]);
102
+ }
103
+
104
+ return swaggerInfo;
105
+ }
@@ -0,0 +1,72 @@
1
+ export class AuthException extends Error {
2
+ private id: string = "";
3
+ get Id(): string {
4
+ return this.id;
5
+ }
6
+
7
+ constructor(id: string, message: string = "") {
8
+ super(message);
9
+ this.id = id;
10
+ }
11
+ }
12
+
13
+ export class ForbiddenException extends Error {
14
+ }
15
+
16
+ export class InputErrorException extends Error {
17
+ private errorId: string = "";
18
+ get ErrorId(): string {
19
+ return this.errorId;
20
+ }
21
+
22
+ constructor(errorId: string, message: string = "") {
23
+ super(message);
24
+ this.errorId = errorId;
25
+ }
26
+ }
27
+
28
+ export class MaintenanceException extends Error {
29
+
30
+ constructor(message: string = "") {
31
+ super(message);
32
+ }
33
+ }
34
+
35
+ export class NotFoundException extends Error {
36
+ // for 404 Not Found
37
+ private errorId: string = "";
38
+ get ErrorId(): string {
39
+ return this.errorId;
40
+ }
41
+
42
+ constructor(errorId: string, message: string = "") {
43
+ super(message);
44
+ this.errorId = errorId;
45
+ }
46
+ }
47
+
48
+ export class DbConflictException extends Error {
49
+ // for 409 Conflict
50
+ private errorId: string = "";
51
+ get ErrorId(): string {
52
+ return this.errorId;
53
+ }
54
+
55
+ constructor(errorId: string, message: string = "") {
56
+ super(message);
57
+ this.errorId = errorId;
58
+ }
59
+ }
60
+
61
+ export class UnprocessableException extends Error {
62
+ // for 422 Unprocessable Entity
63
+ private errorId: string = "";
64
+ get ErrorId(): string {
65
+ return this.errorId;
66
+ }
67
+
68
+ constructor(errorId: string, message: string = "") {
69
+ super(message);
70
+ this.errorId = errorId;
71
+ }
72
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export { Service } from './Service';
2
+ export { MaintenanceException, AuthException, InputErrorException, ForbiddenException, DbConflictException, UnprocessableException, NotFoundException } from './exceptions/Exception';
3
+ export { createSwagger } from './documents/Swagger';
4
+ export { AwsS3Client } from './clients/AwsS3Client';
5
+ export { Base64Client } from './clients/Base64Client';
6
+ export { StringClient } from './clients/StringClient';
7
+ export { EncryptClient } from './clients/EncryptClient';
8
+
9
+ export { RequestType } from './reqestResponse/RequestType';
10
+ export { ResponseType } from './reqestResponse/ResponseType';
11
+ export { PropertyType } from './reqestResponse/ReqResType';
12
+
13
+ // models
14
+ export { TableModel } from './models/TableModel';
15
+ export { createTableDoc } from './models/TableDoc';
16
+ export { MigrateTable } from './models/MigrateTable';
17
+ export { MigrateDatabase } from './models/MigrateDatabase';
18
+ export { migrate, rollback } from './models/MigrateRollback';
19
+
20
+ // cron
21
+ export { DayType, MonthType, DateType, HourType, MinuteSecondType } from './cron/CronType';
22
+ export { BaseCron } from './cron/BaseCron';
23
+ export { runCron } from './cron/CronExecuter';
@@ -0,0 +1,72 @@
1
+ import { TableModel } from "./TableModel";
2
+ import { TColumnInfo } from "./Type";
3
+
4
+ export default class ExpressionClient {
5
+ private model: TableModel;
6
+ constructor(model: TableModel) {
7
+ this.model = model;
8
+ }
9
+
10
+ public toSqlStringValue(value: any): string {
11
+ if (typeof value === 'string' || typeof value === 'number') {
12
+ return `'${value}'`;
13
+ }
14
+
15
+ throw new Error('Please enter a value of type string or number.');
16
+ }
17
+
18
+ public toSqlNumberValue(value: any): string {
19
+ if (typeof value === 'number') {
20
+ return value.toString();
21
+ } else if (typeof value === 'string') {
22
+ if (value.trim() !== "" || isNaN(Number(value)) === false) {
23
+ return value;
24
+ }
25
+ }
26
+
27
+ throw new Error('Please enter a value of type number or a string of half-width digits.');
28
+ }
29
+
30
+ public createCaseFromObject(column: string | TColumnInfo, obj: {[key: string | number]: string | number | boolean | null}, elseValue: string | number | boolean | null): string {
31
+ const columnInfo = typeof column === 'string' ? this.model.getColumn(column) : column.model.getColumn(column.name);
32
+ if (columnInfo.type !== 'string' && columnInfo.type !== 'integer') {
33
+ throw new Error('This method cannot be used for columns other than integer, real, or string.');
34
+ }
35
+
36
+ const whenExpression = Object.entries(obj).map(([key, value]) => {
37
+ let expression = `WHEN ${columnInfo.expression} = `;
38
+ // 今は文字列と数値だけだが、他のも対応しないといけないかも
39
+ if (columnInfo.type === 'string') {
40
+ expression += this.toSqlStringValue(key);
41
+ } else {
42
+ expression += this.toSqlNumberValue(key);
43
+ }
44
+ expression += ` THEN `;
45
+
46
+ if (value === null) {
47
+ expression += 'null';
48
+ } else if (typeof value === 'string') {
49
+ expression += this.toSqlStringValue(value);
50
+ } else if (typeof value === 'boolean') {
51
+ expression += `${value}`;
52
+ } else {
53
+ expression += `${value}`;
54
+ }
55
+
56
+ return expression;
57
+ });
58
+
59
+ let elseExpression = `ELSE `;
60
+ if (elseValue === null) {
61
+ elseExpression += 'null';
62
+ } else if (typeof elseValue === 'string') {
63
+ elseExpression += this.toSqlStringValue(elseValue);
64
+ } else if (typeof elseValue === 'boolean') {
65
+ elseExpression += `${elseValue}`;
66
+ } else {
67
+ elseExpression += `${elseValue}`;
68
+ }
69
+
70
+ return `CASE ${whenExpression.join(' ')} ${elseExpression} END`;
71
+ }
72
+ }