@oino-ts/common 0.21.1 → 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 (72) hide show
  1. package/README.md +183 -0
  2. package/dist/cjs/OINOApi.js +322 -0
  3. package/dist/cjs/OINOBenchmark.js +3 -4
  4. package/dist/cjs/OINOConfig.js +104 -0
  5. package/dist/cjs/OINOConstants.js +42 -0
  6. package/dist/cjs/OINODataField.js +346 -0
  7. package/dist/cjs/OINODataModel.js +182 -0
  8. package/dist/cjs/OINODataSource.js +165 -0
  9. package/dist/cjs/OINOFormatter.js +6 -5
  10. package/dist/cjs/OINOHtmlTemplate.js +21 -18
  11. package/dist/cjs/OINOModelSet.js +333 -0
  12. package/dist/cjs/OINOParser.js +448 -0
  13. package/dist/cjs/OINOQueryParams.js +434 -0
  14. package/dist/cjs/OINORequest.js +21 -13
  15. package/dist/cjs/OINOResult.js +13 -12
  16. package/dist/cjs/OINOStr.js +11 -11
  17. package/dist/cjs/OINOSwagger.js +205 -0
  18. package/dist/cjs/index.js +57 -39
  19. package/dist/esm/OINOApi.js +315 -0
  20. package/dist/esm/OINOBenchmark.js +3 -4
  21. package/dist/esm/OINOConfig.js +100 -0
  22. package/dist/esm/OINOConstants.js +39 -0
  23. package/dist/esm/OINODataField.js +337 -0
  24. package/dist/esm/OINODataModel.js +178 -0
  25. package/dist/esm/OINODataSource.js +159 -0
  26. package/dist/esm/OINOFormatter.js +2 -1
  27. package/dist/esm/OINOHtmlTemplate.js +4 -1
  28. package/dist/esm/OINOModelSet.js +329 -0
  29. package/dist/esm/OINOParser.js +444 -0
  30. package/dist/esm/OINOQueryParams.js +426 -0
  31. package/dist/esm/OINORequest.js +9 -1
  32. package/dist/esm/OINOResult.js +2 -1
  33. package/dist/esm/OINOStr.js +1 -1
  34. package/dist/esm/OINOSwagger.js +201 -0
  35. package/dist/esm/index.js +14 -32
  36. package/dist/types/OINOApi.d.ts +191 -0
  37. package/dist/types/OINOBenchmark.d.ts +1 -1
  38. package/dist/types/OINOConfig.d.ts +63 -0
  39. package/dist/types/OINOConstants.d.ts +51 -0
  40. package/dist/types/OINODataField.d.ts +209 -0
  41. package/dist/types/OINODataModel.d.ts +78 -0
  42. package/dist/types/OINODataSource.d.ts +184 -0
  43. package/dist/types/OINOHtmlTemplate.d.ts +1 -1
  44. package/dist/types/OINOModelSet.d.ts +64 -0
  45. package/dist/types/OINOParser.d.ts +42 -0
  46. package/dist/types/OINOQueryParams.d.ts +270 -0
  47. package/dist/types/OINORequest.d.ts +4 -1
  48. package/dist/types/OINOResult.d.ts +1 -1
  49. package/dist/types/OINOStr.d.ts +1 -1
  50. package/dist/types/OINOSwagger.d.ts +25 -0
  51. package/dist/types/index.d.ts +14 -31
  52. package/package.json +32 -32
  53. package/src/OINOApi.ts +429 -0
  54. package/src/OINOBenchmark.ts +323 -324
  55. package/src/OINOConfig.ts +113 -0
  56. package/src/OINOConstants.ts +59 -0
  57. package/src/OINODataField.ts +371 -0
  58. package/src/OINODataModel.ts +187 -0
  59. package/src/OINODataSource.ts +280 -0
  60. package/src/OINOFormatter.ts +166 -165
  61. package/src/OINOHeaders.ts +51 -51
  62. package/src/OINOHtmlTemplate.test.ts +114 -114
  63. package/src/OINOHtmlTemplate.ts +225 -222
  64. package/src/OINOLog.ts +292 -292
  65. package/src/OINOModelSet.ts +359 -0
  66. package/src/OINOParser.ts +441 -0
  67. package/src/OINOQueryParams.ts +449 -0
  68. package/src/OINORequest.ts +204 -196
  69. package/src/OINOResult.ts +331 -330
  70. package/src/OINOStr.ts +254 -254
  71. package/src/OINOSwagger.ts +213 -0
  72. package/src/index.ts +18 -38
@@ -0,0 +1,359 @@
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 { OINODataRow, OINOContentType, OINODataCell } from "./OINOConstants.js"
8
+ import { OINOConfig } from "./OINOConfig.js"
9
+ import { OINOStr } from "./OINOStr.js"
10
+ import { OINOLog } from "./OINOLog.js"
11
+ import { OINODataSet } from "./OINODataSource.js"
12
+ import { OINODataModel } from "./OINODataModel.js"
13
+ import { OINODataField, OINOBlobDataField, OINONumberDataField, OINOBooleanDataField } from "./OINODataField.js"
14
+ import { OINOQueryParams } from "./OINOQueryParams.js"
15
+
16
+ /**
17
+ * Class for dataset based on a data model that can be serialized to
18
+ * a supported format:
19
+ * - JSON (application/json)
20
+ * - CSV (text/csv)
21
+ *
22
+ */
23
+ export class OINOModelSet {
24
+
25
+ /** Reference to datamodel */
26
+ readonly datamodel: OINODataModel
27
+
28
+ /** Reference to data set */
29
+ readonly dataset: OINODataSet
30
+
31
+ /** SQL parameters */
32
+ readonly queryParams?: OINOQueryParams
33
+
34
+ /** Collection of errors */
35
+ errors: string[]
36
+
37
+ /**
38
+ * Constructor for `OINOModelSet`.
39
+ *
40
+ * @param datamodel data model
41
+ * @param dataset data set
42
+ * @param queryParams SQL parameters
43
+ */
44
+ constructor(datamodel: OINODataModel, dataset: OINODataSet, queryParams?: OINOQueryParams) {
45
+ this.datamodel = datamodel
46
+ this.dataset = dataset
47
+ this.queryParams = queryParams
48
+ this.errors = this.dataset.messages
49
+ }
50
+
51
+ private _encodeAndHashFieldValue(field:OINODataField, value:string|null, contentType:OINOContentType, primaryKeyValues:string[], rowIdSeed:string):string {
52
+ let result:string
53
+ if (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) {
54
+ if (value && (field instanceof OINONumberDataField) && (this.datamodel.api.hashid) && ((this.queryParams?.aggregate === undefined) || (this.queryParams.aggregate.isAggregated(field.name) == false))) {
55
+ value = this.datamodel.api.hashid.encode(value, rowIdSeed)
56
+ }
57
+ if (field.fieldParams.isPrimaryKey) {
58
+ primaryKeyValues.push(value || "")
59
+ }
60
+ }
61
+ result = OINOStr.encode(value, contentType)
62
+ return result
63
+ }
64
+
65
+ private _writeRowJson(row:OINODataRow):string {
66
+ // console.log("OINOModelSet._writeRowJson: row=" + row)
67
+ const model:OINODataModel = this.datamodel
68
+ const fields:OINODataField[] = model.fields
69
+ let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
70
+ let primary_key_values:string[] = []
71
+ let json_row:string = ""
72
+ for (let i=0; i<fields.length; i++) {
73
+ const f = fields[i]
74
+ if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
75
+ continue
76
+ }
77
+ let value:string|null|undefined = f.serializeCell(row[i])
78
+ if (value === undefined) {
79
+ // skip undefined values
80
+
81
+ } else if (value === null) {
82
+ json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":null"
83
+
84
+ } else {
85
+
86
+ let is_hashed:boolean = (f.fieldParams.isPrimaryKey || f.fieldParams.isForeignKey) && (f instanceof OINONumberDataField) && (this.datamodel.api.hashid != null)
87
+ let is_value = (f instanceof OINOBooleanDataField) || ((f instanceof OINONumberDataField) && !is_hashed)
88
+ value = this._encodeAndHashFieldValue(f, value, OINOContentType.json, primary_key_values, f.name + " " + row_id_seed)
89
+ if (is_value) {
90
+ value = value.substring(1, value.length-1)
91
+ }
92
+ json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":" + value
93
+ }
94
+ }
95
+ json_row = OINOStr.encode(OINOConfig.OINO_ID_FIELD, OINOContentType.json) + ":" + OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.json) + json_row
96
+ return "{" + json_row + "}"
97
+ }
98
+
99
+ private async _writeStringJson():Promise<string> {
100
+ let result:string = ""
101
+ while (!this.dataset.isEof()) {
102
+ if (result != "") {
103
+ result += ",\r\n"
104
+ }
105
+ const row:OINODataRow = this.dataset.getRow()
106
+ result += this._writeRowJson(row)
107
+ await this.dataset.next()
108
+ }
109
+ result = "[\r\n" + result + "\r\n]"
110
+ return result
111
+ }
112
+
113
+ private _writeHeaderCsv():string {
114
+ const model:OINODataModel = this.datamodel
115
+ const fields:OINODataField[] = model.fields
116
+ let csv_header:string = "\"" + OINOConfig.OINO_ID_FIELD + "\""
117
+ for (let i=0; i<fields.length; i++) {
118
+ if ((this.queryParams?.select?.isSelected(fields[i].name) === false) && (fields[i].fieldParams.isPrimaryKey == false)) {
119
+ continue
120
+ }
121
+ csv_header += ",\"" + fields[i].name + "\""
122
+ }
123
+ return csv_header
124
+ }
125
+
126
+ private _writeRowCsv(row:OINODataRow):string {
127
+ const model:OINODataModel = this.datamodel
128
+ const fields:OINODataField[] = model.fields
129
+ let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
130
+ let primary_key_values:string[] = []
131
+ let csv_row:string = ""
132
+ for (let i=0; i<fields.length; i++) {
133
+ const f = fields[i]
134
+ if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
135
+ continue
136
+ }
137
+ let value:string|null|undefined = f.serializeCell(row[i])
138
+ if (value == null) {
139
+ csv_row += "," + OINOStr.encode(value, OINOContentType.csv) // either null or undefined
140
+
141
+ } else {
142
+ value = this._encodeAndHashFieldValue(f, value, OINOContentType.csv, primary_key_values, f.name + " " + row_id_seed)
143
+ csv_row += "," + value
144
+ }
145
+ }
146
+ csv_row = OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.csv) + csv_row
147
+ return csv_row
148
+ }
149
+
150
+ private async _writeStringCsv():Promise<string> {
151
+ let result:string = this._writeHeaderCsv()
152
+ while (!this.dataset.isEof()) {
153
+ if (result != "") {
154
+ result += "\r\n"
155
+ }
156
+ const row:OINODataRow = this.dataset.getRow()
157
+ result += this._writeRowCsv(row)
158
+ await this.dataset.next()
159
+ }
160
+ return result
161
+ }
162
+
163
+ private _writeRowFormdataParameterBlock(blockName:string, blockValue:string|null, multipartBoundary:string):string {
164
+ if (blockValue === null) {
165
+ return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n"
166
+ } else {
167
+ return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n" + blockValue + "\r\n"
168
+ }
169
+ }
170
+
171
+ private _writeRowFormdataFileBlock(blockName:string, blockValue:string, multipartBoundary:string):string {
172
+ return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"; filename=" + blockName + "\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: BASE64\r\n\r\n" + blockValue + "\r\n"
173
+ }
174
+
175
+ private _writeRowFormdata(row:OINODataRow):string {
176
+ const multipart_boundary:string = "---------OINOMultipartBoundary35424568" // this method is just used for test data generation and we want it to be static
177
+ const model:OINODataModel = this.datamodel
178
+ const fields:OINODataField[] = model.fields
179
+ let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
180
+ let primary_key_values:string[] = []
181
+ let result:string = ""
182
+ for (let i=0; i<fields.length; i++) {
183
+ const f = fields[i]
184
+ if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
185
+ continue
186
+ }
187
+ let value:string|null|undefined = f.serializeCell(row[i])
188
+ let formdata_block:string = ""
189
+ let is_file = (f instanceof OINOBlobDataField)
190
+
191
+ if (value === undefined) {
192
+ OINOLog.info("@oino-ts/db", "OINOModelSet", "_writeRowFormdata", "Undefined value skipped", {field_name:f.name})
193
+
194
+ } else if (value === null) {
195
+ formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, null, multipart_boundary)
196
+
197
+ } else {
198
+ value = this._encodeAndHashFieldValue(f, value, OINOContentType.formdata, primary_key_values, f.name + " " + row_id_seed)
199
+ if (is_file) {
200
+ formdata_block = this._writeRowFormdataFileBlock(f.name, value, multipart_boundary)
201
+ } else {
202
+ formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, value, multipart_boundary)
203
+ }
204
+ }
205
+
206
+ result += formdata_block
207
+ }
208
+ result = this._writeRowFormdataParameterBlock(OINOConfig.OINO_ID_FIELD, OINOConfig.printOINOId(primary_key_values), multipart_boundary) + result
209
+ return result
210
+ }
211
+
212
+ private _writeStringFormdata():string {
213
+ const row:OINODataRow = this.dataset.getRow()
214
+ let result:string = this._writeRowFormdata(row)
215
+ return result
216
+ }
217
+
218
+
219
+ private _writeRowUrlencode(row:OINODataRow):string {
220
+ const model:OINODataModel = this.datamodel
221
+ const fields:OINODataField[] = model.fields
222
+ let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
223
+ let primary_key_values:string[] = []
224
+ let urlencode_row:string = ""
225
+ for (let i=0; i<fields.length; i++) {
226
+ const f = fields[i]
227
+ if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
228
+ continue
229
+ }
230
+ let value:string|null|undefined = f.serializeCell(row[i])
231
+ if ((value === undefined)) { // || (value === null)) {
232
+ // console.log("OINOModelSet._writeRowUrlencode undefined field value:" + fields[i].name)
233
+ } else {
234
+ value = this._encodeAndHashFieldValue(f, value, OINOContentType.urlencode, primary_key_values, f.name + " " + row_id_seed)
235
+ if (urlencode_row != "") {
236
+ urlencode_row += "&"
237
+ }
238
+ urlencode_row += OINOStr.encode(f.name, OINOContentType.urlencode) + "=" + value
239
+ }
240
+ }
241
+ urlencode_row = OINOStr.encode(OINOConfig.OINO_ID_FIELD, OINOContentType.urlencode) + "=" + OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.urlencode) + "&" + urlencode_row
242
+ return urlencode_row
243
+ }
244
+
245
+ private async _writeStringUrlencode():Promise<string> {
246
+ let result:string = ""
247
+ let line_count = 0
248
+ while (!this.dataset.isEof()) {
249
+ const row:OINODataRow = this.dataset.getRow()
250
+ result += this._writeRowUrlencode(row) + "\r\n"
251
+ await this.dataset.next()
252
+ line_count += 1
253
+ }
254
+ if (line_count > 1) {
255
+ OINOLog.warning("@oino-ts/db", "OINOModelSet", "_writeStringUrlencode", "Content type " + OINOContentType.urlencode + " does not officially support multiline content!", {})
256
+ }
257
+ return result
258
+ }
259
+
260
+ private _exportRow(row:OINODataRow):any {
261
+ // console.log("OINOModelSet._exportRow: row=" + row)
262
+ const model:OINODataModel = this.datamodel
263
+ const fields:OINODataField[] = model.fields
264
+ let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
265
+ let primary_key_values:string[] = []
266
+ let result:any = {}
267
+ for (let i=0; i<fields.length; i++) {
268
+ const f = fields[i]
269
+ if (f.fieldParams.isPrimaryKey) {
270
+ primary_key_values.push(f.serializeCell(row[i]) || "")
271
+ }
272
+ if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
273
+ continue
274
+ }
275
+ let value:OINODataCell = f.datasource.parseValueAsCell(row[i], f.nativeType) // retain original value without serialization
276
+ if (value === undefined) {
277
+ // skip undefined values
278
+
279
+ } else if (value === null) { // differentiate null and undefined
280
+ result[f.name] = null
281
+
282
+ } else {
283
+ result[f.name] = value
284
+ }
285
+ }
286
+ result[OINOConfig.OINO_ID_FIELD] = OINOConfig.printOINOId(primary_key_values)
287
+ return result
288
+ }
289
+
290
+
291
+ /**
292
+ * Serialize model set in the given format.
293
+ *
294
+ * @param [contentType=OINOContentType.json] serialization content type
295
+ *
296
+ */
297
+ async writeString(contentType:OINOContentType = OINOContentType.json):Promise<string> {
298
+ let result:string = ""
299
+ if (contentType == OINOContentType.csv) {
300
+ result += await this._writeStringCsv()
301
+
302
+ } else if (contentType == OINOContentType.json) {
303
+ result += await this._writeStringJson()
304
+
305
+ } else if (contentType == OINOContentType.formdata) {
306
+ result += await this._writeStringFormdata()
307
+
308
+ } else if (contentType == OINOContentType.urlencode) {
309
+ result += await this._writeStringUrlencode()
310
+
311
+ } else {
312
+ OINOLog.error("@oino-ts/db", "OINOModelSet", "writeString", "Content type is only for input!", {contentType:contentType})
313
+ }
314
+ return result
315
+ }
316
+
317
+ /**
318
+ * Get value of given field in the current row. Undefined if no rows,
319
+ * field not found or value does not exist.
320
+ *
321
+ * @param fieldName name of the field
322
+ * @param serialize serialize the value
323
+ *
324
+ */
325
+ getValueByFieldName(fieldName:string, serialize:boolean = false):OINODataCell {
326
+ let result:OINODataCell = undefined
327
+ if (!this.dataset.isEof()) {
328
+ const current_row:OINODataRow = this.dataset.getRow()
329
+ const field_index:number = this.datamodel.findFieldIndexByName(fieldName)
330
+ if (field_index >= 0) {
331
+ result = current_row[field_index]
332
+ if (serialize) {
333
+ result = this.datamodel.fields[field_index].serializeCell(result)
334
+ }
335
+ }
336
+ }
337
+ return result
338
+ }
339
+
340
+ /**
341
+ * Export all rows as a record with OINOId as key and object with row cells as values.
342
+ *
343
+ * @param idFieldName optional field name to use as key instead of OINOId
344
+ */
345
+
346
+ async exportAsRecord(idFieldName?:string):Promise<Record<string, any>> {
347
+ const result:Record<string, any> = {}
348
+ const row_id_field = idFieldName || OINOConfig.OINO_ID_FIELD
349
+ while (!this.dataset.isEof()) {
350
+ const row_data:OINODataRow = this.dataset.getRow()
351
+ const row_export = this._exportRow(row_data)
352
+ const row_id = row_export[row_id_field]
353
+ result[row_id] = row_export
354
+ await this.dataset.next()
355
+ }
356
+ return result
357
+ }
358
+
359
+ }