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,421 @@
1
+ import { TableModel } from "../TableModel";
2
+ import { TColumnArrayType, TColumnDetail, TColumnInfo, TColumnType, TNestedCondition, TOperator, TQuery } from "../Type";
3
+ import ValidateValueUtil from "./ValidateValueUtil";
4
+
5
+ export default class WhereExpression {
6
+
7
+ public static createConditionPk(model: TableModel, pk: {[key: string]: any}, vars: Array<any> | null = null, isSetAlias: boolean = false): TQuery {
8
+ const conditions = [];
9
+ const newVars = vars === null ? [] : [...vars];
10
+
11
+ for (const [keyColumn, column] of Object.entries(model.Columns)) {
12
+ if (column.attribute !== 'primary') {
13
+ continue;
14
+ }
15
+
16
+ if (pk[keyColumn] === undefined || pk[keyColumn] === null) {
17
+ throw new Error(`No value is set for the primary key "${model.TableName}".${keyColumn}.`);
18
+ }
19
+
20
+ ValidateValueUtil.validateValue(column, pk[keyColumn]);
21
+ newVars.push(pk[keyColumn]);
22
+ conditions.push(`${isSetAlias ? `"${model.TableAlias}".` : ''}${keyColumn} = $${newVars.length}`);
23
+ }
24
+
25
+ return {
26
+ expression: conditions.join(' AND '),
27
+ vars: newVars
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Helper method to create OR conditions
33
+ * @param conditions Array of conditions that make up the OR condition
34
+ * @returns SQL query string representing the OR condition
35
+ */
36
+ public static createCondition(conditions: Array<TNestedCondition>, model: TableModel, varLength: number): TQuery {
37
+
38
+ if (conditions.length === 0) {
39
+ return { expression: '' };
40
+ }
41
+
42
+ let logicalOperator = 'AND';
43
+ if (conditions[0] === 'AND' || conditions[0] === 'OR') {
44
+ if (conditions.length === 1) {
45
+ return { expression: '' };
46
+ }
47
+
48
+ logicalOperator = conditions[0];
49
+ conditions.shift();
50
+ }
51
+
52
+ const expression: string[] = [];
53
+ let vars: any[] = []
54
+ for (let condition of conditions) {
55
+ if (Array.isArray(condition)) {
56
+ // If it's an array, it's a nested condition, so call this function recursively
57
+ const query = this.createCondition(condition, model, varLength + vars.length);
58
+ expression.push(query.expression);
59
+ if (query.vars !== undefined) {
60
+ vars = [...vars, ...query.vars];
61
+ }
62
+ continue;
63
+ }
64
+
65
+ if (typeof condition === 'string') {
66
+ // If specified directly as a string, it becomes a query, so insert as is
67
+ expression.push(condition);
68
+ continue;
69
+ }
70
+
71
+ if (typeof condition.l === 'string') {
72
+ const query = this.create({ model: model, name: condition.l}, condition.o, condition.r, varLength + vars.length);
73
+ expression.push(query.expression);
74
+ if (query.vars !== undefined) {
75
+ vars = [...vars, ...query.vars];
76
+ }
77
+ continue;
78
+ }
79
+
80
+ const query = this.create(condition.l, condition.o, condition.r, varLength + vars.length);
81
+ expression.push(query.expression);
82
+ if (query.vars !== undefined) {
83
+ vars = [...vars, ...query.vars];
84
+ }
85
+ }
86
+
87
+ return {
88
+ expression: `(${expression.filter(condition => condition ?? '' !== '').join(` ${logicalOperator} `)})`,
89
+ vars: vars
90
+ }
91
+ }
92
+
93
+ public static create(left: TColumnInfo, operator: TOperator, right: TColumnInfo | any, varLength: number) : TQuery {
94
+
95
+ // Check if the specified ColumnInfo exists
96
+ const leftColumn = left.model.getColumn(left.name);
97
+
98
+ // Are the operators correct?
99
+ const useableOperator: { [key in TColumnType | TColumnArrayType]: TOperator[] } = {
100
+ integer: ["=", "!=", ">", ">=", "<", "<=", "in", "not in"],
101
+ 'integer[]': ["=", "any", "@>", "&&"],
102
+ real: ["=", "!=", ">", ">=", "<", "<="],
103
+ 'real[]': ["=", "any", "@>", "&&"],
104
+ string: ["=", "!=", "like", "ilike", "h2f_like", "h2f_ilike", "in", "not in"],
105
+ 'string[]': ["=", "any", "@>", "&&"],
106
+ uuid: ["=", "!=", "in", "not in"],
107
+ 'uuid[]': ["=", "any", "@>", "&&"],
108
+ bool: ["=", "!=", "in", "not in"],
109
+ 'bool[]': ["=", "any", "@>", "&&"],
110
+ date: ["=", "!=", ">", ">=", "<", "<="],
111
+ 'date[]': ["=", "any", "@>", "&&"],
112
+ time: ["=", "!=", ">", ">=", "<", "<="],
113
+ 'time[]': ["=", "any", "@>", "&&"],
114
+ timestamp: ["=", "!=", ">", ">=", "<", "<="],
115
+ 'timestamp[]': ["=", "any", "@>", "&&"],
116
+ json: [],
117
+ 'json[]': [],
118
+ jsonb: [],
119
+ 'jsonb[]': []
120
+ };
121
+
122
+ if (useableOperator[leftColumn.type].includes(operator) == false) {
123
+ throw new Error(`The ${operator} operator cannot be used for ${leftColumn.tableName}.${leftColumn.columnName}. (${leftColumn.type})`);
124
+ }
125
+
126
+ if (right === null) {
127
+ if (leftColumn.attribute !== "nullable") {
128
+ throw new Error(`You cannot use conditions with null values unless the attribute is nullable.`);
129
+ }
130
+
131
+ if (operator == "=") {
132
+ return {
133
+ expression: `${leftColumn.expression} is null`
134
+ }
135
+ } else if (operator == "!=") {
136
+ return {
137
+ expression: `${leftColumn.expression} is not null`
138
+ }
139
+ } else {
140
+ throw new Error(`When comparing with null, operators other than =, != cannot be used. (${operator})`);
141
+ }
142
+ }
143
+
144
+ const isColumnRight = right !== null && typeof right === 'object' && 'model' in right && 'name' in right;
145
+ const isArrayColumnLeft = leftColumn.type.endsWith("[]");
146
+ if (isArrayColumnLeft) {
147
+ if (isColumnRight) {
148
+ return this.createExpressionArrayColumn(leftColumn, operator, right, varLength);
149
+ } else {
150
+ return this.createExpressionArrayValue(leftColumn, operator, right, varLength);
151
+ }
152
+ } else {
153
+ return this.createExpression(leftColumn, operator, right, varLength);
154
+ }
155
+ }
156
+
157
+ private static createExpression(leftColumn: TColumnDetail, operator: TOperator, right: TColumnInfo | any, varLength: number) : TQuery {
158
+ // IN NOT IN clause
159
+ if (["in", "not in"].includes(operator)) {
160
+ if (Array.isArray(right) == false) {
161
+ throw new Error(`For the 'in' operator, you cannot input anything other than an array on the right side.`);
162
+ }
163
+
164
+ if (right.length == 0) {
165
+ // Creating in, not in with 0 elements will cause an error, but since the data to be passed is correct and the expected return value does not change, do not search if there are 0 elements
166
+ return { expression: '' };
167
+ }
168
+
169
+ // Validate values
170
+ for (const value of right) {
171
+ ValidateValueUtil.validateValue(leftColumn, value);
172
+ }
173
+ return {
174
+ expression: `${leftColumn.expression} ${operator === 'in' ? '=' : '!='} ANY($${varLength})`,
175
+ vars: [right]
176
+ }
177
+ } else if (Array.isArray(right)) {
178
+ throw new Error(`For operators other than 'in', you cannot input an array on the right side.`);
179
+ }
180
+
181
+ // If the right side value is a column specification
182
+ if (right !== null && typeof right === 'object' && 'model' in right && 'name' in right) {
183
+ const rightColumn = right.model.getColumn(right.name);
184
+
185
+ // 型の不一致エラーメッセージを改善
186
+ if (leftColumn.type !== rightColumn.type) {
187
+ throw new Error(`Type mismatch: column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
188
+ }
189
+
190
+ // LIKE operators are different, so handle separately
191
+ switch (operator) {
192
+ case 'like':
193
+ case 'ilike':
194
+ return {
195
+ expression: `${leftColumn.expression} ${operator} '%' || ${rightColumn.expression} || '%'`
196
+ }
197
+ case 'h2f_like': // half to full like
198
+ case 'h2f_ilike': // half to full ilike
199
+ return {
200
+ expression: `${this.makeSqlReplaceHalfToFull(leftColumn.expression)} ${operator.replace("h2f_", "")} ${this.makeSqlReplaceHalfToFull(`'%' || ${rightColumn.expression} || '%'`)}`
201
+ }
202
+ }
203
+
204
+ return {
205
+ expression: `${leftColumn.expression} ${operator} ${rightColumn.expression}`
206
+ }
207
+ }
208
+
209
+ ValidateValueUtil.validateValue(leftColumn, right);
210
+ // LIKE operators are different, so handle separately
211
+ switch (operator) {
212
+ case 'like':
213
+ case 'ilike':
214
+ return {
215
+ expression: `${leftColumn.expression} ${operator} $${varLength}`,
216
+ vars: [`%${right}%`]
217
+ }
218
+ case 'h2f_like': // half to full like
219
+ case 'h2f_ilike': // half to full ilike
220
+ return {
221
+ expression: `${this.makeSqlReplaceHalfToFull(leftColumn.expression)} ${operator.replace("h2f_", "")} ${this.makeSqlReplaceHalfToFull(`$${varLength}`)}`,
222
+ vars: [`%${right}%`]
223
+ }
224
+ }
225
+
226
+ return {
227
+ expression: `${leftColumn.expression} ${operator} $${varLength}`,
228
+ vars: [right]
229
+ }
230
+ }
231
+
232
+ private static createExpressionArrayValue(leftColumn: TColumnDetail, operator: TOperator, right: any, varLength: number) : TQuery {
233
+
234
+ // バリデーションチェック
235
+ switch (operator) {
236
+ case 'any':
237
+ switch (leftColumn.type) {
238
+ case 'integer[]':
239
+ if (ValidateValueUtil.isErrorInteger(right)) {
240
+ throw new Error(`Expected integer value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
241
+ }
242
+ break;
243
+ case 'real[]':
244
+ if (ValidateValueUtil.isErrorReal(right)) {
245
+ throw new Error(`Expected numeric value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
246
+ }
247
+ break;
248
+ case 'string[]':
249
+ if (ValidateValueUtil.isErrorString(right)) {
250
+ throw new Error(`Expected string value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
251
+ }
252
+ break;
253
+ case 'uuid[]':
254
+ if (ValidateValueUtil.isErrorUUID(right)) {
255
+ throw new Error(`Expected UUID value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
256
+ }
257
+ break;
258
+ case 'bool[]':
259
+ if (ValidateValueUtil.isErrorBool(right)) {
260
+ throw new Error(`Expected boolean value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
261
+ }
262
+ break;
263
+ case 'date[]':
264
+ if (ValidateValueUtil.isErrorDate(right)) {
265
+ throw new Error(`Expected date value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
266
+ }
267
+ break;
268
+ case 'time[]':
269
+ if (ValidateValueUtil.isErrorTime(right)) {
270
+ throw new Error(`Expected time value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
271
+ }
272
+ break;
273
+ case 'timestamp[]':
274
+ if (ValidateValueUtil.isErrorTimestamp(right)) {
275
+ throw new Error(`Expected timestamp value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
276
+ }
277
+ break;
278
+ }
279
+ break;
280
+ case '=':
281
+ case '@>':
282
+ case '&&':
283
+ if (Array.isArray(right) === false) {
284
+ throw new Error(`Expected array format for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
285
+ }
286
+
287
+ for (const value of right) {
288
+ switch (leftColumn.type) {
289
+ case 'integer[]':
290
+ if (ValidateValueUtil.isErrorInteger(value)) {
291
+ throw new Error(`Expected integer value in array element, but received: ${JSON.stringify(value)}`);
292
+ }
293
+ break;
294
+ case 'real[]':
295
+ if (ValidateValueUtil.isErrorReal(value)) {
296
+ throw new Error(`Expected numeric value in array element, but received: ${JSON.stringify(value)}`);
297
+ }
298
+ break;
299
+ case 'string[]':
300
+ if (ValidateValueUtil.isErrorString(value)) {
301
+ throw new Error(`Expected string value in array element, but received: ${JSON.stringify(value)}`);
302
+ }
303
+ break;
304
+ case 'uuid[]':
305
+ if (ValidateValueUtil.isErrorUUID(value)) {
306
+ throw new Error(`Expected UUID value in array element, but received: ${JSON.stringify(value)}`);
307
+ }
308
+ break;
309
+ case 'bool[]':
310
+ if (ValidateValueUtil.isErrorBool(value)) {
311
+ throw new Error(`Expected boolean value in array element, but received: ${JSON.stringify(value)}`);
312
+ }
313
+ break;
314
+ case 'date[]':
315
+ if (ValidateValueUtil.isErrorDate(value)) {
316
+ throw new Error(`Expected date value in array element, but received: ${JSON.stringify(value)}`);
317
+ }
318
+ break;
319
+ case 'time[]':
320
+ if (ValidateValueUtil.isErrorTime(value)) {
321
+ throw new Error(`Expected time value in array element, but received: ${JSON.stringify(value)}`);
322
+ }
323
+ break;
324
+ case 'timestamp[]':
325
+ if (ValidateValueUtil.isErrorTimestamp(value)) {
326
+ throw new Error(`Expected timestamp value in array element, but received: ${JSON.stringify(value)}`);
327
+ }
328
+ break;
329
+ }
330
+ }
331
+ break;
332
+ default:
333
+ throw new Error(`Unsupported operator '${operator}' for array column operations.`);
334
+ }
335
+
336
+ switch (operator) {
337
+ case '=':
338
+ // =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
339
+ return {
340
+ expression: `(${leftColumn.expression} @> $${varLength} AND $${varLength} @> ${leftColumn.expression})`,
341
+ vars: [right]
342
+ }
343
+ case '@>':
344
+ case '&&':
345
+ return {
346
+ expression: `${leftColumn.expression} ${operator} $${varLength}`,
347
+ vars: [right]
348
+ }
349
+ case 'any':
350
+ return {
351
+ expression: `$${varLength} = ANY(${leftColumn.expression})`,
352
+ vars: [right]
353
+ }
354
+ }
355
+ }
356
+
357
+ private static createExpressionArrayColumn(leftColumn: TColumnDetail, operator: TOperator, right: TColumnInfo, varLength: number) : TQuery {
358
+
359
+ const rightColumn = right.model.getColumn(right.name);
360
+
361
+ // バリデーションチェック
362
+ switch (operator) {
363
+ case 'any':
364
+ // any演算子の場合
365
+ if (leftColumn.type !== rightColumn.type.replace('[]', '')) {
366
+ throw new Error(`Type mismatch: array column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and scalar column [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) are incompatible for ANY operation.`);
367
+ }
368
+ break;
369
+ case '=':
370
+ case '@>':
371
+ case '&&':
372
+ // 配列演算子の場合
373
+ if (leftColumn.type !== rightColumn.type) {
374
+ throw new Error(`Type mismatch: array columns [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
375
+ }
376
+ break;
377
+ default:
378
+ // サポートされていない演算子
379
+ throw new Error(`Operator '${operator}' is not supported for array column operations. Supported operators: =, @>, &&, ANY`);
380
+ }
381
+
382
+ switch (operator) {
383
+ case '=':
384
+ // =で検索すると順番まで一致させないといけないので、順番不一致でも中身があってれば一致とするようにする
385
+ return {
386
+ expression: `(${leftColumn.expression} @> ${rightColumn.expression} AND ${rightColumn.expression} @> ${leftColumn.expression})`,
387
+ vars: []
388
+ }
389
+ case '@>':
390
+ case '&&':
391
+ return {
392
+ expression: `${leftColumn.expression} ${operator} $${rightColumn.expression}`,
393
+ vars: []
394
+ }
395
+ case 'any':
396
+ return {
397
+ expression: `$${rightColumn.expression} = ANY(${leftColumn.expression})`,
398
+ vars: []
399
+ }
400
+ }
401
+ }
402
+
403
+ /**
404
+ * SQL statement to convert half-width characters to full-width
405
+ * @param {string} columnName Column name
406
+ * @returns SQL statement
407
+ */
408
+ private static makeSqlReplaceHalfToFull(columnNameOrValue: string) {
409
+ let objs: { [key: string]: string } = {
410
+ '0123456789': '0123456789',
411
+ 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポァィゥェォャュョッー、。・「」゛゜': 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンヴガギグゲゴザジズゼゾダヂヅデドハハビブベボパピプペポァィゥェォャュョッー、。・「」 ゙ ゚',
412
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
413
+ 'abcdefghijklmnopqrstuvwxyz': 'abcdefghijklmnopqrstuvwxyz'
414
+ };
415
+
416
+ let sql = columnNameOrValue;
417
+ Object.keys(objs).forEach(key => sql = `TRANSLATE(${sql} ,'${key}','${objs[key]}')`);
418
+
419
+ return sql;
420
+ }
421
+ }