@oino-ts/db 0.0.17 → 0.1.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 (44) hide show
  1. package/README.md +4 -7
  2. package/dist/cjs/OINODb.js +132 -1
  3. package/dist/cjs/OINODbApi.js +15 -12
  4. package/dist/cjs/OINODbDataField.js +7 -1
  5. package/dist/cjs/OINODbDataModel.js +1 -1
  6. package/dist/cjs/OINODbFactory.js +0 -356
  7. package/dist/cjs/OINODbModelSet.js +1 -1
  8. package/dist/cjs/OINODbParser.js +466 -0
  9. package/dist/cjs/OINODbRequestParams.js +24 -18
  10. package/dist/cjs/OINODbSqlParams.js +336 -0
  11. package/dist/cjs/index.js +26 -25
  12. package/dist/esm/OINODb.js +129 -0
  13. package/dist/esm/OINODbApi.js +15 -12
  14. package/dist/esm/OINODbDataField.js +7 -1
  15. package/dist/esm/OINODbDataModel.js +2 -2
  16. package/dist/esm/OINODbFactory.js +1 -357
  17. package/dist/esm/OINODbModelSet.js +1 -1
  18. package/dist/esm/OINODbParser.js +462 -0
  19. package/dist/esm/OINODbRequestParams.js +24 -18
  20. package/dist/esm/OINODbSqlParams.js +330 -0
  21. package/dist/esm/index.js +5 -5
  22. package/package.json +4 -3
  23. package/src/OINODb.ts +163 -1
  24. package/src/OINODbApi.test.ts +182 -44
  25. package/src/OINODbApi.ts +16 -14
  26. package/src/OINODbDataField.ts +7 -2
  27. package/src/OINODbDataModel.ts +1 -1
  28. package/src/OINODbFactory.ts +1 -358
  29. package/src/OINODbModelSet.ts +1 -1
  30. package/src/OINODbParser.ts +458 -0
  31. package/src/{OINODbRequestParams.ts → OINODbSqlParams.ts} +24 -16
  32. package/src/index.ts +6 -6
  33. package/dist/types/OINODb.d.ts +0 -86
  34. package/dist/types/OINODbApi.d.ts +0 -82
  35. package/dist/types/OINODbConfig.d.ts +0 -52
  36. package/dist/types/OINODbDataField.d.ts +0 -202
  37. package/dist/types/OINODbDataModel.d.ts +0 -108
  38. package/dist/types/OINODbDataSet.d.ts +0 -95
  39. package/dist/types/OINODbFactory.d.ts +0 -63
  40. package/dist/types/OINODbModelSet.d.ts +0 -51
  41. package/dist/types/OINODbRequestParams.d.ts +0 -146
  42. package/dist/types/OINODbSwagger.d.ts +0 -25
  43. package/dist/types/index.d.ts +0 -111
  44. package/src/OINODbDataSet.ts +0 -167
@@ -4,7 +4,7 @@
4
4
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
6
 
7
- import { OINOHttpResult, OINODbApi, OINODbApiParams, OINODbParams, OINOContentType, OINODbDataModel, OINODbDataField, OINODb, OINODataRow, OINODbConstructor, OINODbApiRequestParams, OINODbSqlFilter, OINOStr, OINOBlobDataField, OINODbApiResult, OINODbDataSet, OINODbModelSet, OINODbConfig, OINONumberDataField, OINODataCell, OINODbSqlOrder, OINODbSqlLimit, OINO_ERROR_PREFIX, OINO_WARNING_PREFIX, OINO_INFO_PREFIX, OINO_DEBUG_PREFIX, OINOLog, OINODbSqlParams, OINOResult } from "./index.js"
7
+ import { OINODbApi, OINODbApiParams, OINODbParams, OINOContentType, OINODb, OINODbConstructor, OINODbApiRequestParams, OINODbSqlFilter, OINODbConfig, OINODbSqlOrder, OINODbSqlLimit, OINODbSqlParams } from "./index.js"
8
8
 
9
9
  /**
10
10
  * Static factory class for easily creating things based on data
@@ -117,361 +117,4 @@ export class OINODbFactory {
117
117
  // OINOLog.debug("createParamsFromRequest", {params:result})
118
118
  return result
119
119
  }
120
-
121
- private static _findCsvLineEnd(csvData:string, start:number):number {
122
- const n:number = csvData.length
123
- if (start >= n) {
124
- return start
125
- }
126
- let end:number = start
127
- let quote_open:boolean = false
128
- while (end<n) {
129
- if (csvData[end] == "\"") {
130
- if (!quote_open) {
131
- quote_open = true
132
- } else if ((end < n-1) && (csvData[end+1] == "\"")) {
133
- end++
134
- } else {
135
- quote_open = false
136
- }
137
- } else if ((!quote_open) && (csvData[end] == "\r")) {
138
- return end
139
- }
140
- end++
141
- }
142
- return n
143
- }
144
-
145
- private static _parseCsvLine(csvLine:string):(string|null|undefined)[] {
146
- let result:(string|null|undefined)[] = []
147
- const n:number = csvLine.length
148
- let start:number = 0
149
- let end:number = 0
150
- let quote_open:boolean = false
151
- let has_quotes:boolean = false
152
- let has_escaped_quotes = false
153
- let found_field = false
154
- while (end<n) {
155
- if (csvLine[end] == "\"") {
156
- if (!quote_open) {
157
- quote_open = true
158
- } else if ((end < n-1) && (csvLine[end+1] == "\"")) {
159
- end++
160
- has_escaped_quotes = true
161
- } else {
162
- has_quotes = true
163
- quote_open = false
164
- }
165
- }
166
- if ((!quote_open) && ((end == n-1) || (csvLine[end] == ","))) {
167
- found_field = true
168
- if (end == n-1) {
169
- end++
170
- }
171
- }
172
- if (found_field) {
173
- // console.log("OINODB_csvParseLine: next field=" + csvLine.substring(start,end) + ", start="+start+", end="+end)
174
- let field_str:string|undefined|null
175
- if (has_quotes) {
176
- field_str = csvLine.substring(start+1,end-1)
177
- } else if (start == end) {
178
- field_str = undefined
179
- } else {
180
- field_str = csvLine.substring(start,end)
181
- if (field_str == "null") {
182
- field_str = null
183
- }
184
- }
185
- result.push(field_str)
186
- has_quotes = false
187
- has_escaped_quotes = true
188
- found_field = false
189
- start = end+1
190
- }
191
- end++
192
- }
193
- return result
194
- }
195
-
196
- private static createRowFromCsv(datamodel:OINODbDataModel, data:string):OINODataRow[] {
197
- let result:OINODataRow[] = []
198
- const n = data.length
199
- let start:number = 0
200
- let end:number = this._findCsvLineEnd(data, start)
201
- const header_str = data.substring(start, end)
202
- const headers:(string|null|undefined)[] = this._parseCsvLine(header_str)
203
- let field_to_header_mapping:number[] = new Array(datamodel.fields.length)
204
- let headers_found:boolean = false
205
- for (let i=0; i<field_to_header_mapping.length; i++) {
206
- field_to_header_mapping[i] = headers.indexOf(datamodel.fields[i].name)
207
- headers_found = headers_found || (field_to_header_mapping[i] >= 0)
208
- }
209
- // OINOLog.debug("createRowFromCsv", {headers:headers, field_to_header_mapping:field_to_header_mapping})
210
- if (!headers_found) {
211
- return result
212
- }
213
- start = end + 1
214
- end = start
215
- while (end < n) {
216
- while ((start < n) && ((data[start] == "\r") || (data[start] == "\n"))) {
217
- start++
218
- }
219
- if (start >= n) {
220
- return result
221
- }
222
- end = this._findCsvLineEnd(data, start)
223
- const row_data:(string|null|undefined)[] = this._parseCsvLine(data.substring(start, end))
224
- const row:OINODataRow = new Array(field_to_header_mapping.length)
225
- for (let i=0; i<datamodel.fields.length; i++) {
226
- const field:OINODbDataField = datamodel.fields[i]
227
- let j:number = field_to_header_mapping[i]
228
- let value:string|null|undefined = row_data[j]
229
- if ((value === undefined) || (value === null)) { // null/undefined-decoding built into the parser
230
- row[i] = value
231
-
232
- } else if ((j >= 0) && (j < row_data.length)) {
233
- value = OINOStr.decode(value, OINOContentType.csv)
234
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
235
- value = datamodel.api.hashid.decode(value)
236
- }
237
- row[i] = field.deserializeCell(value)
238
-
239
- } else {
240
- row[i] = undefined
241
- }
242
- }
243
- // console.log("createRowFromCsv: next row=" + row)
244
- result.push(row)
245
- start = end
246
- end = start
247
- }
248
-
249
- return result
250
- }
251
-
252
- private static _createRowFromJsonObj(obj:any, datamodel:OINODbDataModel):OINODataRow {
253
- // console.log("createRowFromJsonObj: obj=" + JSON.stringify(obj))
254
- const fields:OINODbDataField[] = datamodel.fields
255
- let result:OINODataRow = new Array(fields.length)
256
- // console.log("createRowFromJsonObj: " + result)
257
- for (let i=0; i < fields.length; i++) {
258
- const field = fields[i]
259
- let value:any = obj[field.name]
260
- // console.log("createRowFromJsonObj: key=" + field.name + ", val=" + val)
261
- if ((value === null) || (value === undefined)) { // must be checed first as null is an object
262
- result[i] = value
263
-
264
- } else if (Array.isArray(value) || typeof value === "object") {
265
- result[i] = JSON.stringify(value).replaceAll("\"","\\\"") // only single level deep objects, rest is handled as JSON-strings
266
-
267
- } else if (typeof value === "string") {
268
- value = OINOStr.decode(value, OINOContentType.json)
269
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
270
- value = datamodel.api.hashid.decode(value)
271
- }
272
- result[i] = field.deserializeCell(value)
273
-
274
- } else {
275
- result[i] = value // value types are passed as-is
276
- }
277
- // console.log("createRowFromJsonObj: result["+i+"]=" + result[i])
278
- }
279
- // console.log("createRowFromJsonObj: " + result)
280
- return result
281
- }
282
-
283
- private static _createRowFromJson(datamodel:OINODbDataModel, data:string):OINODataRow[] {
284
- try {
285
- let result:OINODataRow[] = []
286
- // console.log("OINORowFactoryJson: data=" + data)
287
- const obj:object = JSON.parse(data)
288
- if (Array.isArray(obj)) {
289
- obj.forEach(row => {
290
- result.push(this._createRowFromJsonObj(row, datamodel))
291
- });
292
-
293
- } else {
294
- result.push(this._createRowFromJsonObj(obj, datamodel))
295
- }
296
- return result
297
-
298
- } catch (e:any) {
299
- return []
300
- }
301
- }
302
-
303
- private static _findMultipartBoundary(formData:string, multipartBoundary:string, start:number):number {
304
- let n:number = formData.indexOf(multipartBoundary, start)
305
- if (n >= 0) {
306
- n += multipartBoundary.length + 2
307
- } else {
308
- n = formData.length
309
- }
310
- return n
311
- }
312
-
313
- private static _parseMultipartLine(csvData:string, start:number):string {
314
- let line_end:number = csvData.indexOf('\r\n', start)
315
- if (line_end >= start) {
316
- return csvData.substring(start, line_end)
317
- } else {
318
- return ''
319
- }
320
- }
321
-
322
- private static _multipartHeaderRegex:RegExp = /Content-Disposition\: (form-data|file); name=\"([^\"]+)\"(; filename=.*)?/i
323
-
324
- private static createRowFromFormdata(datamodel:OINODbDataModel, data:string, multipartBoundary:string):OINODataRow[] {
325
- let result:OINODataRow[] = []
326
- const n = data.length
327
- let start:number = this._findMultipartBoundary(data, multipartBoundary, 0)
328
- let end:number = this._findMultipartBoundary(data, multipartBoundary, start)
329
- // OINOLog.debug("createRowFromFormdata: enter", {start:start, end:end, multipartBoundary:multipartBoundary})
330
- const row:OINODataRow = new Array(datamodel.fields.length)
331
- while (end < n) {
332
- // OINOLog.debug("createRowFromFormdata: next block", {start:start, end:end, block:data.substring(start, end)})
333
- let block_ok:boolean = true
334
- let l:string = this._parseMultipartLine(data, start)
335
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
336
- start += l.length+2
337
- const header_matches = OINODbFactory._multipartHeaderRegex.exec(l)
338
- if (!header_matches) {
339
- OINOLog.warning("OINODbFactory.createRowFromFormdata: unsupported block skipped!", {header_line:l})
340
- block_ok = false
341
-
342
- } else {
343
- const field_name = header_matches[2]
344
- const is_file = header_matches[3] != null
345
- let is_base64:boolean = false
346
- const field_index:number = datamodel.findFieldIndexByName(field_name)
347
- // OINOLog.debug("createRowFromFormdata: header", {field_name:field_name, field_index:field_index, is_file:is_file})
348
- if (field_index < 0) {
349
- OINOLog.warning("OINODbFactory.createRowFromFormdata: form field not found and skipped!", {field_name:field_name})
350
- block_ok = false
351
-
352
- } else {
353
- const field:OINODbDataField = datamodel.fields[field_index]
354
- l = this._parseMultipartLine(data, start)
355
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
356
- while (block_ok && (l != '')) {
357
- if (l.startsWith('Content-Type:') && (l.indexOf('multipart/mixed')>=0)) {
358
- OINOLog.warning("OINODbFactory.createRowFromFormdata: mixed multipart files not supported and skipped!", {header_line:l})
359
- block_ok = false
360
- } else if (l.startsWith('Content-Transfer-Encoding:') && (l.indexOf('BASE64')>=0)) {
361
- is_base64 = true
362
- }
363
- start += l.length+2
364
- l = this._parseMultipartLine(data, start)
365
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
366
- }
367
- start += 2
368
- if (!block_ok) {
369
- OINOLog.warning("OINODbFactory.createRowFromFormdata: invalid block skipped", {field_name:field_name})
370
- } else if (start + multipartBoundary.length + 2 >= end) {
371
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: null value", {field_name:field_name})
372
- row[field_index] = null
373
-
374
- } else if (is_file) {
375
- const value = this._parseMultipartLine(data, start).trim()
376
- if (is_base64) {
377
- row[field_index] = field.deserializeCell(OINOStr.decode(value, OINOContentType.formdata))
378
- } else {
379
- row[field_index] = Buffer.from(value, "binary")
380
- }
381
- } else {
382
- let value:string = OINOStr.decode(this._parseMultipartLine(data, start).trim(), OINOContentType.formdata)
383
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: parse form field", {field_name:field_name, value:value})
384
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
385
- value = datamodel.api.hashid.decode(value)
386
- }
387
- row[field_index] = field.deserializeCell(value)
388
- }
389
- }
390
- }
391
- start = end
392
- end = this._findMultipartBoundary(data, multipartBoundary, start)
393
- }
394
- // OINOLog.debug("createRowFromFormdata: next row", {row:row})
395
- result.push(row)
396
-
397
- return result
398
- }
399
- private static createRowFromUrlencoded(datamodel:OINODbDataModel, data:string):OINODataRow[] {
400
- // OINOLog.debug("createRowFromUrlencoded: enter", {data:data})
401
- let result:OINODataRow[] = []
402
- const row:OINODataRow = new Array(datamodel.fields.length)
403
- const data_parts:string[] = data.trim().split('&')
404
- for (let i=0; i<data_parts.length; i++) {
405
- const param_parts = data_parts[i].split('=')
406
- // OINOLog.debug("createRowFromUrlencoded: next param", {param_parts:param_parts})
407
- if (param_parts.length == 2) {
408
- const key=OINOStr.decodeUrlencode(param_parts[0]) || ""
409
- const field_index:number = datamodel.findFieldIndexByName(key)
410
- if (field_index < 0) {
411
- OINOLog.info("createRowFromUrlencoded: param field not found", {field:key})
412
-
413
- } else {
414
- const field:OINODbDataField = datamodel.fields[field_index]
415
- let value:string=OINOStr.decode(param_parts[1], OINOContentType.urlencode)
416
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
417
- value = datamodel.api.hashid.decode(value)
418
- }
419
- row[field_index] = field.deserializeCell(value)
420
- }
421
- }
422
-
423
- // const value = requestParams[]
424
-
425
- }
426
- // console.log("createRowFromUrlencoded: next row=" + row)
427
- result.push(row)
428
- return result
429
- }
430
-
431
- /**
432
- * Create data rows from request body based on the datamodel.
433
- *
434
- * @param datamodel datamodel of the api
435
- * @param data data as a string
436
- * @param requestParams parameters
437
- *
438
- */
439
- static createRows(datamodel:OINODbDataModel, data:string, requestParams:OINODbApiRequestParams ):OINODataRow[] {
440
- if ((requestParams.requestType == OINOContentType.json) || (requestParams.requestType == undefined)) {
441
- return this._createRowFromJson(datamodel, data)
442
-
443
- } else if (requestParams.requestType == OINOContentType.csv) {
444
- return this.createRowFromCsv(datamodel, data)
445
-
446
- } else if (requestParams.requestType == OINOContentType.formdata) {
447
- return this.createRowFromFormdata(datamodel, data, requestParams.multipartBoundary || "")
448
-
449
- } else if (requestParams.requestType == OINOContentType.urlencode) {
450
- return this.createRowFromUrlencoded(datamodel, data)
451
-
452
- } else if (requestParams.requestType == OINOContentType.html) {
453
- OINOLog.error("HTML can't be used as an input content type!", {contentType:OINOContentType.html})
454
- return []
455
- } else {
456
- OINOLog.error("Unrecognized input content type!", {contentType:requestParams.requestType})
457
- return []
458
- }
459
- }
460
- /**
461
- * Create one data row from javascript object based on the datamodel.
462
- * NOTE! Data assumed to be unserialized i.e. of the native type (string, number, boolean, Buffer)
463
- *
464
- * @param datamodel datamodel of the api
465
- * @param data data as javascript object
466
- *
467
- */
468
- static createRowFromObject(datamodel:OINODbDataModel, data:any):OINODataRow {
469
- const fields:OINODbDataField[] = datamodel.fields
470
- let result:OINODataRow = new Array(fields.length)
471
- for (let i=0; i < fields.length; i++) {
472
- result[i] = data[fields[i].name]
473
- }
474
- return result
475
- }
476
-
477
120
  }
@@ -61,7 +61,7 @@ export class OINODbModelSet {
61
61
  const f = fields[i]
62
62
  let value:string|null|undefined = f.serializeCell(row[i])
63
63
  if (value === undefined) {
64
- OINOLog.info("OINODbModelSet._writeRowJson: undefined value skipped", {field_name:f.name})
64
+ // OINOLog.info("OINODbModelSet._writeRowJson: undefined value skipped", {field_name:f.name})
65
65
 
66
66
  } else if (value === null) {
67
67
  json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":null"