@oino-ts/common 0.21.2 → 1.0.0

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 (69) hide show
  1. package/README.md +183 -0
  2. package/dist/cjs/OINOApi.js +322 -0
  3. package/dist/cjs/OINOConfig.js +104 -0
  4. package/dist/cjs/OINOConstants.js +42 -0
  5. package/dist/cjs/OINODataField.js +346 -0
  6. package/dist/cjs/OINODataModel.js +182 -0
  7. package/dist/cjs/OINODataSource.js +165 -0
  8. package/dist/cjs/OINOFormatter.js +6 -5
  9. package/dist/cjs/OINOHtmlTemplate.js +21 -18
  10. package/dist/cjs/OINOModelSet.js +333 -0
  11. package/dist/cjs/OINOParser.js +448 -0
  12. package/dist/cjs/OINOQueryParams.js +434 -0
  13. package/dist/cjs/OINORequest.js +21 -13
  14. package/dist/cjs/OINOResult.js +13 -12
  15. package/dist/cjs/OINOStr.js +11 -11
  16. package/dist/cjs/OINOSwagger.js +205 -0
  17. package/dist/cjs/index.js +57 -39
  18. package/dist/esm/OINOApi.js +315 -0
  19. package/dist/esm/OINOConfig.js +100 -0
  20. package/dist/esm/OINOConstants.js +39 -0
  21. package/dist/esm/OINODataField.js +337 -0
  22. package/dist/esm/OINODataModel.js +178 -0
  23. package/dist/esm/OINODataSource.js +159 -0
  24. package/dist/esm/OINOFormatter.js +2 -1
  25. package/dist/esm/OINOHtmlTemplate.js +4 -1
  26. package/dist/esm/OINOModelSet.js +329 -0
  27. package/dist/esm/OINOParser.js +444 -0
  28. package/dist/esm/OINOQueryParams.js +426 -0
  29. package/dist/esm/OINORequest.js +9 -1
  30. package/dist/esm/OINOResult.js +2 -1
  31. package/dist/esm/OINOStr.js +1 -1
  32. package/dist/esm/OINOSwagger.js +201 -0
  33. package/dist/esm/index.js +14 -32
  34. package/dist/types/OINOApi.d.ts +191 -0
  35. package/dist/types/OINOConfig.d.ts +63 -0
  36. package/dist/types/OINOConstants.d.ts +51 -0
  37. package/dist/types/OINODataField.d.ts +209 -0
  38. package/dist/types/OINODataModel.d.ts +78 -0
  39. package/dist/types/OINODataSource.d.ts +184 -0
  40. package/dist/types/OINOHtmlTemplate.d.ts +1 -1
  41. package/dist/types/OINOModelSet.d.ts +64 -0
  42. package/dist/types/OINOParser.d.ts +42 -0
  43. package/dist/types/OINOQueryParams.d.ts +270 -0
  44. package/dist/types/OINORequest.d.ts +4 -1
  45. package/dist/types/OINOResult.d.ts +1 -1
  46. package/dist/types/OINOStr.d.ts +1 -1
  47. package/dist/types/OINOSwagger.d.ts +25 -0
  48. package/dist/types/index.d.ts +14 -31
  49. package/package.json +32 -32
  50. package/src/OINOApi.ts +429 -0
  51. package/src/OINOBenchmark.ts +323 -323
  52. package/src/OINOConfig.ts +113 -0
  53. package/src/OINOConstants.ts +59 -0
  54. package/src/OINODataField.ts +371 -0
  55. package/src/OINODataModel.ts +187 -0
  56. package/src/OINODataSource.ts +280 -0
  57. package/src/OINOFormatter.ts +166 -165
  58. package/src/OINOHeaders.ts +51 -51
  59. package/src/OINOHtmlTemplate.test.ts +114 -114
  60. package/src/OINOHtmlTemplate.ts +225 -222
  61. package/src/OINOLog.ts +292 -292
  62. package/src/OINOModelSet.ts +359 -0
  63. package/src/OINOParser.ts +441 -0
  64. package/src/OINOQueryParams.ts +449 -0
  65. package/src/OINORequest.ts +204 -196
  66. package/src/OINOResult.ts +331 -330
  67. package/src/OINOStr.ts +254 -254
  68. package/src/OINOSwagger.ts +213 -0
  69. package/src/index.ts +18 -38
@@ -0,0 +1,449 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import { OINO_ERROR_PREFIX } from "./OINOConstants.js"
8
+ import { OINOStr } from "./OINOStr.js"
9
+ import { OINOLog } from "./OINOLog.js"
10
+
11
+ const OINO_FIELD_NAME_CHARS:string = "\\w\\s\\-\\_\\#\\¤"
12
+
13
+ /**
14
+ * Supported logical conjunctions in filter predicates.
15
+ * @enum
16
+ */
17
+ export enum OINOQueryBooleanOperation { and = "and", or = "or", not = "not" }
18
+
19
+ /**
20
+ * Supported logical conjunctions in filter predicates.
21
+ * @enum
22
+ */
23
+ export enum OINOQueryComparison { lt = "lt", le = "le", eq = "eq", ne = "ne", ge = "ge", gt = "gt", like = "like" }
24
+
25
+ /**
26
+ * Supported logical conjunctions in filter predicates.
27
+ * @enum
28
+ */
29
+ export enum OINOQueryNullCheck { isnull = "isnull", isNotNull = "isNotNull" }
30
+
31
+ /**
32
+ * Supported aggregation functions in OINODbQueryAggregate.
33
+ * @enum
34
+ */
35
+ export enum OINOQueryAggregateFunctions { count = "count", sum = "sum", avg = "avg", min = "min", max = "max" }
36
+
37
+ /**
38
+ * Class for recursively parsing of filters and printing them as SQL conditions.
39
+ * Supports three types of statements
40
+ * - comparison: (field)-lt|le|eq|ge|gt|like(value)
41
+ * - negation: -not(filter)
42
+ * - conjunction/disjunction: (filter)-and|or(filter)
43
+ * Supported conditions are comparisons (<, <=, =, >=, >) and substring match (LIKE).
44
+ *
45
+ */
46
+ export class OINOQueryFilter {
47
+ protected static _booleanOperationRegex = /^\s?\-(and|or)\s?$/i
48
+ protected static _negationRegex = /^-(not)\((.+)\)$/i
49
+ protected static _filterComparisonRegex = /^\(([^'"\(\)]+)\)\s?\-(lt|le|eq|ne|ge|gt|like)\s?\(([^'"\(\)]+)\)$/i
50
+ protected static _filterNullCheckRegex = /^-(isnull|isNotNull)\((.+)\)$/i
51
+
52
+ readonly leftSide: OINOQueryFilter | string
53
+ readonly rightSide: OINOQueryFilter | string
54
+ readonly operator: OINOQueryComparison|OINOQueryBooleanOperation|OINOQueryNullCheck|null
55
+
56
+ /**
57
+ * Constructor of `OINOQueryFilter`
58
+ * @param leftSide left side of the filter, either another filter or a column name
59
+ * @param operation operation of the filter, either `OINOQueryComparison` or `OINOQueryBooleanOperation`
60
+ * @param rightSide right side of the filter, either another filter or a value
61
+ */
62
+ constructor(leftSide:OINOQueryFilter|string, operation:OINOQueryComparison|OINOQueryBooleanOperation|OINOQueryNullCheck|null, rightSide:OINOQueryFilter|string) {
63
+ if (!(
64
+ ((operation === null) && (leftSide == "") && (rightSide == "")) ||
65
+ ((operation !== null) && (Object.values(OINOQueryComparison).includes(operation as OINOQueryComparison)) && (typeof(leftSide) == "string") && (leftSide != "") && (typeof(rightSide) == "string") && (rightSide != "")) ||
66
+ ((operation == OINOQueryBooleanOperation.not) && (leftSide == "") && (rightSide instanceof OINOQueryFilter)) ||
67
+ (((operation == OINOQueryNullCheck.isnull) || (operation == OINOQueryNullCheck.isNotNull)) && (typeof(leftSide) == "string") && (rightSide == "")) ||
68
+ (((operation == OINOQueryBooleanOperation.and) || (operation == OINOQueryBooleanOperation.or)) && (leftSide instanceof OINOQueryFilter) && (rightSide instanceof OINOQueryFilter))
69
+ )) {
70
+ OINOLog.error("@oino-ts/db", "OINOQueryFilter", "constructor", "Unsupported OINOQueryFilter format", {leftSide:leftSide, operation:operation, rightSide:rightSide})
71
+ throw new Error(OINO_ERROR_PREFIX + ": Unsupported OINOQueryFilter format!")
72
+ }
73
+ this.leftSide = leftSide
74
+ this.operator = operation
75
+ this.rightSide = rightSide
76
+ }
77
+
78
+ /**
79
+ * Constructor for `OINOQueryFilter` as parser of http parameter.
80
+ *
81
+ * Supports three types of statements:
82
+ * - comparison: (field)-lt|le|eq|ge|gt|like(value)
83
+ * - negation: -not(filter)
84
+ * - conjunction/disjunction: (filter)-and|or(filter)
85
+ * - null check: -isnull(field) or -isNotNull(field)
86
+ *
87
+ * @param filterString string representation of filter from HTTP-request
88
+ *
89
+ */
90
+ static parse(filterString: string):OINOQueryFilter {
91
+ if (!filterString) {
92
+ return new OINOQueryFilter("", null, "")
93
+
94
+ } else {
95
+ let match = OINOQueryFilter._filterComparisonRegex.exec(filterString)
96
+ if ((match != null) && (match.length == 4)) {
97
+ return new OINOQueryFilter(match[1], match[2].toLowerCase() as OINOQueryComparison, match[3])
98
+
99
+ } else {
100
+ let match = OINOQueryFilter._negationRegex.exec(filterString)
101
+ if (match != null) {
102
+ return new OINOQueryFilter("", OINOQueryBooleanOperation.not, OINOQueryFilter.parse(match[3]))
103
+
104
+ } else {
105
+ let boolean_parts = OINOStr.splitByBrackets(filterString, true, false, '(', ')')
106
+ if (boolean_parts.length == 3 && (boolean_parts[1].match(OINOQueryFilter._booleanOperationRegex))) {
107
+ return new OINOQueryFilter(OINOQueryFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1) as OINOQueryBooleanOperation, OINOQueryFilter.parse(boolean_parts[2]))
108
+
109
+ } else {
110
+ let match = OINOQueryFilter._filterNullCheckRegex.exec(filterString)
111
+ if ((match != null)) {
112
+ return new OINOQueryFilter(match[2], match[1].toLowerCase() as OINOQueryComparison, "")
113
+
114
+ } else {
115
+ OINOLog.error("@oino-ts/db", "OINOQueryFilter", "constructor", "Invalid filter", {filterString:filterString})
116
+ throw new Error(OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'") // invalid filter could be a security risk, stop processing
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Construct a new `OINOQueryFilter` as combination of (boolean and/or) of two filters.
126
+ *
127
+ * @param leftSide left side to combine
128
+ * @param operation boolean operation to use in combination
129
+ * @param rightSide right side to combine
130
+ *
131
+ */
132
+ static combine(leftSide:OINOQueryFilter|undefined, operation:OINOQueryBooleanOperation, rightSide:OINOQueryFilter|undefined):OINOQueryFilter|undefined {
133
+ if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
134
+ return new OINOQueryFilter(leftSide, operation, rightSide)
135
+
136
+ } else if ((leftSide) && (!leftSide.isEmpty())) {
137
+ return leftSide
138
+
139
+ } else if ((rightSide) && (!rightSide.isEmpty())) {
140
+ return rightSide
141
+
142
+ } else {
143
+ return undefined
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Combine two filters with an AND operation.
149
+ *
150
+ * @param leftSide left side filter
151
+ * @param rightSide right side filter
152
+ *
153
+ */
154
+ static and(leftSide:OINOQueryFilter, rightSide:OINOQueryFilter):OINOQueryFilter|undefined {
155
+ if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
156
+ return new OINOQueryFilter(leftSide, OINOQueryBooleanOperation.and, rightSide)
157
+
158
+ } else {
159
+ return undefined
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Combine two filters with an OR operation.
165
+ *
166
+ * @param leftSide left side filter
167
+ * @param rightSide right side filter
168
+ *
169
+ */
170
+ static or(leftSide:OINOQueryFilter, rightSide:OINOQueryFilter):OINOQueryFilter|undefined {
171
+ if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
172
+ return new OINOQueryFilter(leftSide, OINOQueryBooleanOperation.or, rightSide)
173
+
174
+ } else {
175
+ return undefined
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Negate a filter with a NOT operation.
181
+ *
182
+ * @param leftSide left side filter
183
+ *
184
+ */
185
+ static not(leftSide:OINOQueryFilter):OINOQueryFilter|undefined {
186
+ if ((leftSide) && (!leftSide.isEmpty())) {
187
+ return new OINOQueryFilter(leftSide, OINOQueryBooleanOperation.not, "")
188
+
189
+ } else {
190
+ return undefined
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Does filter contain any valid conditions.
196
+ *
197
+ */
198
+ isEmpty():boolean {
199
+ return (this.leftSide == "") && (this.operator == null) && (this.rightSide == "")
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Class for ordering select results on a number of columns.
205
+ *
206
+ */
207
+ export class OINOQueryOrder {
208
+ protected static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC|\+|\-)?\s*?$/i
209
+
210
+ readonly columns: string[]
211
+ readonly descending: boolean[]
212
+
213
+ /**
214
+ * Constructor for `OINOQueryOrder`.
215
+ *
216
+ * @param column_or_array single or array of columns to order on
217
+ * @param descending_or_array single or array of booleans if ordes is descending
218
+ *
219
+ */
220
+ constructor(column_or_array:string[]|string, descending_or_array:boolean[]|boolean) {
221
+ if (Array.isArray(column_or_array)) {
222
+ this.columns = column_or_array
223
+ } else {
224
+ this.columns = [column_or_array]
225
+ }
226
+ if (Array.isArray(descending_or_array)) {
227
+ this.descending = descending_or_array
228
+ } else {
229
+ this.descending = [descending_or_array]
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Constructor for `OINOQueryOrder` as parser of http parameter.
235
+ *
236
+ * Supports comma separated list of column orders formatted as :
237
+ * - `column` - order by column in ascending order
238
+ * - `column ASC|DESC` - order by single either ascending or descending order
239
+ * - `column+|-` - order by single either ascending or descending order
240
+ *
241
+ * @param orderString string representation of order from HTTP-request
242
+ *
243
+ */
244
+ static parse(orderString: string):OINOQueryOrder {
245
+ let columns:string[] = []
246
+ let directions:boolean[] = []
247
+
248
+ const column_strings = orderString.split(',')
249
+
250
+ for (let i=0; i<column_strings.length; i++) {
251
+ let match = OINOQueryOrder._orderColumnRegex.exec(column_strings[i])
252
+ if (match != null) {
253
+ columns.push(match[1])
254
+ const dir:string = (match[2] || "ASC").toUpperCase()
255
+ directions.push((dir == "DESC") || (dir == "-"))
256
+ }
257
+ }
258
+ return new OINOQueryOrder(columns, directions)
259
+ }
260
+
261
+ /**
262
+ * Does filter contain any valid conditions.
263
+ *
264
+ */
265
+ isEmpty():boolean {
266
+ return (this.columns.length == 0)
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Class for limiting the number of results.
272
+ *
273
+ */
274
+ export class OINOQueryLimit {
275
+ protected static _limitRegex = /^(\d+)(\spage\s|\.)?(\d+)?$/i
276
+
277
+ readonly limit: number
278
+ readonly page: number
279
+
280
+ /**
281
+ * Constructor for `OINOQueryLimit`.
282
+ *
283
+ * @param limit maximum number of items to return
284
+ * @param page page number to return starting from 1
285
+ *
286
+ */
287
+ constructor(limit: number, page: number = -1) {
288
+ this.limit = limit
289
+ this.page = page
290
+ }
291
+ /**
292
+ * Constructor for `OINOQueryLimit` as parser of http parameter.
293
+ *
294
+ * Supports limit and page formatted as:
295
+ * - `limit` - limit number of items to return
296
+ * - `limit page n` - limit number of items to return and return page n (starting from 1)
297
+ * - `limit.n` - limit number of items to return and return page n (starting from 1)
298
+ *
299
+ * @param limitString string representation of limit from HTTP-request
300
+ *
301
+ */
302
+ static parse(limitString: string):OINOQueryLimit {
303
+ let match = OINOQueryLimit._limitRegex.exec(limitString)
304
+ if ((match != null) && (match.length == 4)) {
305
+ return new OINOQueryLimit(Number.parseInt(match[1]), Number.parseInt(match[3]))
306
+ } else if (match != null) {
307
+ return new OINOQueryLimit(Number.parseInt(match[1]))
308
+ } else {
309
+ return new OINOQueryLimit(-1)
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Does filter contain any valid conditions.
315
+ *
316
+ */
317
+ isEmpty():boolean {
318
+ return (this.limit <= 0)
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Class for limiting the number of results.
324
+ *
325
+ */
326
+ export class OINOQueryAggregate {
327
+ protected static _aggregateRegex:RegExp = new RegExp("^(count|sum|avg|min|max)\\(([" + OINO_FIELD_NAME_CHARS + "]+)\\)$", "mi")
328
+
329
+ readonly functions: OINOQueryAggregateFunctions[]
330
+ readonly fields: string[]
331
+
332
+ /**
333
+ * Constructor for `OINOQueryAggregate`.
334
+ *
335
+ * @param functions aggregate function to use
336
+ * @param fields fields to aggregate
337
+ *
338
+ */
339
+ constructor(functions: OINOQueryAggregateFunctions[], fields: string[]) {
340
+ this.functions = functions
341
+ this.fields = fields
342
+ }
343
+ /**
344
+ * Constructor for `OINOQueryAggregate` as parser of http parameter.
345
+ *
346
+ * Supports comma separated list of aggregates formatted as:
347
+ * - `function(field)`
348
+ *
349
+ * Supported functions are count, sum, avg, min, max.
350
+ *
351
+ * @param aggregatorString string representation of limit from HTTP-request
352
+ *
353
+ */
354
+ static parse(aggregatorString: string):OINOQueryAggregate {
355
+ let funtions:OINOQueryAggregateFunctions[] = []
356
+ let fields:string[] = []
357
+ const aggregator_parts = aggregatorString.split(',')
358
+ for (let i=0; i<aggregator_parts.length; i++) {
359
+ let match = OINOQueryAggregate._aggregateRegex.exec(aggregator_parts[i])
360
+ if ((match != null) && (match.length == 3)) {
361
+ funtions.push(match[1] as OINOQueryAggregateFunctions)
362
+ fields.push(match[2])
363
+ }
364
+ }
365
+ return new OINOQueryAggregate(funtions, fields)
366
+ }
367
+
368
+ /**
369
+ * Does filter contain any valid conditions.
370
+ *
371
+ */
372
+ isEmpty():boolean {
373
+ return (this.functions.length <= 0)
374
+ }
375
+
376
+ /**
377
+ * Does filter contain any valid conditions.
378
+ *
379
+ * @param field field to check if it is aggregated
380
+ */
381
+ isAggregated(field:string):boolean {
382
+ return (this.fields.includes(field))
383
+ }
384
+ }
385
+
386
+
387
+ /**
388
+ * Class for ordering select results on a number of columns.
389
+ *
390
+ */
391
+ export class OINOQuerySelect {
392
+ readonly columns: string[]
393
+
394
+ /**
395
+ * Constructor for `OINOQuerySelect`.
396
+ *
397
+ * @param columns array of columns to select
398
+ *
399
+ */
400
+ constructor(columns:string[]) {
401
+ this.columns = columns
402
+ }
403
+
404
+ /**
405
+ * Constructor for `OINOQuerySelect` as parser of http parameter.
406
+ *
407
+ * @param columns comma separated string selected columns from HTTP-request
408
+ *
409
+ */
410
+ static parse(columns: string):OINOQuerySelect {
411
+ if (columns == "") {
412
+ return new OINOQuerySelect([])
413
+ } else {
414
+ return new OINOQuerySelect(columns.split(','))
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Does select contain any valid columns.
420
+ *
421
+ */
422
+ isEmpty():boolean {
423
+ return (this.columns.length == 0)
424
+ }
425
+
426
+ /**
427
+ * Does select include given column.
428
+ *
429
+ * @param field field to check if it is selected
430
+ *
431
+ */
432
+ isSelected(field:string):boolean {
433
+ return ((this.columns.length == 0) || (this.columns.includes(field)))
434
+ }
435
+ }
436
+
437
+ /** Request options */
438
+ export type OINOQueryParams = {
439
+ /** Additional SQL select where-conditions */
440
+ filter?:OINOQueryFilter,
441
+ /** SQL result ordering conditions */
442
+ order?:OINOQueryOrder
443
+ /** SQL result limit condition */
444
+ limit?:OINOQueryLimit
445
+ /** SQL aggregation functions */
446
+ aggregate?:OINOQueryAggregate
447
+ /** SQL select condition */
448
+ select?:OINOQuerySelect
449
+ }