@oino-ts/common 0.1.0 → 0.1.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oino-ts/common",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "OINO TS package for common classes.",
5
5
  "author": "Matias Kiviniemi (pragmatta)",
6
6
  "license": "MPL-2.0",
@@ -19,7 +19,7 @@
19
19
  "dependencies": {
20
20
  },
21
21
  "devDependencies": {
22
- "@oino-ts/types": "0.1.0"
22
+ "@oino-ts/types": "0.1.2"
23
23
  },
24
24
  "files": [
25
25
  "src/*.ts",
@@ -1,466 +0,0 @@
1
- "use strict";
2
- /*
3
- * This Source Code Form is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.OINOParser = void 0;
9
- const index_js_1 = require("../../db/src/index.js");
10
- /**
11
- * Static factory class for easily creating things based on data
12
- *
13
- */
14
- class OINOParser {
15
- /**
16
- * Create data rows from request body based on the datamodel.
17
- *
18
- * @param datamodel datamodel of the api
19
- * @param data data as a string
20
- * @param requestParams parameters
21
- *
22
- */
23
- static createRows(datamodel, data, requestParams) {
24
- let result = [];
25
- if (typeof data == "string") {
26
- result = this.createRowsFromText(datamodel, data, requestParams);
27
- }
28
- else if (data instanceof Buffer) {
29
- result = this.createRowsFromBlob(datamodel, data, requestParams);
30
- }
31
- else if (typeof data == "object") {
32
- result = [this.createRowFromObject(datamodel, data)];
33
- }
34
- return result;
35
- }
36
- /**
37
- * Create data rows from request body based on the datamodel.
38
- *
39
- * @param datamodel datamodel of the api
40
- * @param data data as a string
41
- * @param requestParams parameters
42
- *
43
- */
44
- static createRowsFromText(datamodel, data, requestParams) {
45
- if ((requestParams.requestType == index_js_1.OINOContentType.json) || (requestParams.requestType == undefined)) {
46
- return this._createRowFromJson(datamodel, data);
47
- }
48
- else if (requestParams.requestType == index_js_1.OINOContentType.csv) {
49
- return this._createRowFromCsv(datamodel, data);
50
- }
51
- else if (requestParams.requestType == index_js_1.OINOContentType.formdata) {
52
- return this._createRowFromFormdata(datamodel, Buffer.from(data, "utf8"), requestParams.multipartBoundary || "");
53
- }
54
- else if (requestParams.requestType == index_js_1.OINOContentType.urlencode) {
55
- return this._createRowFromUrlencoded(datamodel, data);
56
- }
57
- else if (requestParams.requestType == index_js_1.OINOContentType.html) {
58
- index_js_1.OINOLog.error("HTML can't be used as an input content type!", { contentType: index_js_1.OINOContentType.html });
59
- return [];
60
- }
61
- else {
62
- index_js_1.OINOLog.error("Unrecognized input content type!", { contentType: requestParams.requestType });
63
- return [];
64
- }
65
- }
66
- /**
67
- * Create data rows from request body based on the datamodel.
68
- *
69
- * @param datamodel datamodel of the api
70
- * @param data data as an Buffer
71
- * @param requestParams parameters
72
- *
73
- */
74
- static createRowsFromBlob(datamodel, data, requestParams) {
75
- if ((requestParams.requestType == index_js_1.OINOContentType.json) || (requestParams.requestType == undefined)) {
76
- return this._createRowFromJson(datamodel, data.toString()); // JSON is always a string
77
- }
78
- else if (requestParams.requestType == index_js_1.OINOContentType.csv) {
79
- return this._createRowFromCsv(datamodel, data.toString()); // binary data has to be base64 encoded so it's a string
80
- }
81
- else if (requestParams.requestType == index_js_1.OINOContentType.formdata) {
82
- return this._createRowFromFormdata(datamodel, data, requestParams.multipartBoundary || "");
83
- }
84
- else if (requestParams.requestType == index_js_1.OINOContentType.urlencode) {
85
- return this._createRowFromUrlencoded(datamodel, data.toString()); // data is urlencoded so it's a string
86
- }
87
- else if (requestParams.requestType == index_js_1.OINOContentType.html) {
88
- index_js_1.OINOLog.error("HTML can't be used as an input content type!", { contentType: index_js_1.OINOContentType.html });
89
- return [];
90
- }
91
- else {
92
- index_js_1.OINOLog.error("Unrecognized input content type!", { contentType: requestParams.requestType });
93
- return [];
94
- }
95
- }
96
- /**
97
- * Create one data row from javascript object based on the datamodel.
98
- * NOTE! Data assumed to be unserialized i.e. of the native type (string, number, boolean, Buffer)
99
- *
100
- * @param datamodel datamodel of the api
101
- * @param data data as javascript object
102
- *
103
- */
104
- static createRowFromObject(datamodel, data) {
105
- const fields = datamodel.fields;
106
- let result = new Array(fields.length);
107
- for (let i = 0; i < fields.length; i++) {
108
- result[i] = data[fields[i].name];
109
- }
110
- return result;
111
- }
112
- static _findCsvLineEnd(csvData, start) {
113
- const n = csvData.length;
114
- if (start >= n) {
115
- return start;
116
- }
117
- let end = start;
118
- let quote_open = false;
119
- while (end < n) {
120
- if (csvData[end] == "\"") {
121
- if (!quote_open) {
122
- quote_open = true;
123
- }
124
- else if ((end < n - 1) && (csvData[end + 1] == "\"")) {
125
- end++;
126
- }
127
- else {
128
- quote_open = false;
129
- }
130
- }
131
- else if ((!quote_open) && (csvData[end] == "\r")) {
132
- return end;
133
- }
134
- end++;
135
- }
136
- return n;
137
- }
138
- static _parseCsvLine(csvLine) {
139
- let result = [];
140
- const n = csvLine.length;
141
- let start = 0;
142
- let end = 0;
143
- let quote_open = false;
144
- let has_quotes = false;
145
- let has_escaped_quotes = false;
146
- let found_field = false;
147
- while (end < n) {
148
- if (csvLine[end] == "\"") {
149
- if (!quote_open) {
150
- quote_open = true;
151
- }
152
- else if ((end < n - 1) && (csvLine[end + 1] == "\"")) {
153
- end++;
154
- has_escaped_quotes = true;
155
- }
156
- else {
157
- has_quotes = true;
158
- quote_open = false;
159
- }
160
- }
161
- if ((!quote_open) && ((end == n - 1) || (csvLine[end] == ","))) {
162
- found_field = true;
163
- if (end == n - 1) {
164
- end++;
165
- }
166
- }
167
- if (found_field) {
168
- // console.log("OINODB_csvParseLine: next field=" + csvLine.substring(start,end) + ", start="+start+", end="+end)
169
- let field_str;
170
- if (has_quotes) {
171
- field_str = csvLine.substring(start + 1, end - 1);
172
- }
173
- else if (start == end) {
174
- field_str = undefined;
175
- }
176
- else {
177
- field_str = csvLine.substring(start, end);
178
- if (field_str == "null") {
179
- field_str = null;
180
- }
181
- }
182
- result.push(field_str);
183
- has_quotes = false;
184
- has_escaped_quotes = true;
185
- found_field = false;
186
- start = end + 1;
187
- }
188
- end++;
189
- }
190
- return result;
191
- }
192
- static _createRowFromCsv(datamodel, data) {
193
- let result = [];
194
- const n = data.length;
195
- let start = 0;
196
- let end = this._findCsvLineEnd(data, start);
197
- const header_str = data.substring(start, end);
198
- const headers = this._parseCsvLine(header_str);
199
- let field_to_header_mapping = new Array(datamodel.fields.length);
200
- let headers_found = false;
201
- for (let i = 0; i < field_to_header_mapping.length; i++) {
202
- field_to_header_mapping[i] = headers.indexOf(datamodel.fields[i].name);
203
- headers_found = headers_found || (field_to_header_mapping[i] >= 0);
204
- }
205
- // OINOLog.debug("createRowFromCsv", {headers:headers, field_to_header_mapping:field_to_header_mapping})
206
- if (!headers_found) {
207
- return result;
208
- }
209
- start = end + 1;
210
- end = start;
211
- while (end < n) {
212
- while ((start < n) && ((data[start] == "\r") || (data[start] == "\n"))) {
213
- start++;
214
- }
215
- if (start >= n) {
216
- return result;
217
- }
218
- end = this._findCsvLineEnd(data, start);
219
- const row_data = this._parseCsvLine(data.substring(start, end));
220
- const row = new Array(field_to_header_mapping.length);
221
- let has_data = false;
222
- for (let i = 0; i < datamodel.fields.length; i++) {
223
- const field = datamodel.fields[i];
224
- let j = field_to_header_mapping[i];
225
- let value = row_data[j];
226
- if ((value === undefined) || (value === null)) { // null/undefined-decoding built into the parser
227
- row[i] = value;
228
- }
229
- else if ((j >= 0) && (j < row_data.length)) {
230
- value = index_js_1.OINOStr.decode(value, index_js_1.OINOContentType.csv);
231
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof index_js_1.OINONumberDataField) && (datamodel.api.hashid)) {
232
- value = datamodel.api.hashid.decode(value);
233
- }
234
- row[i] = field.deserializeCell(value);
235
- }
236
- else {
237
- row[i] = undefined;
238
- }
239
- has_data = has_data || (row[i] !== undefined);
240
- }
241
- // console.log("createRowFromCsv: next row=" + row)
242
- if (has_data) {
243
- result.push(row);
244
- }
245
- else {
246
- index_js_1.OINOLog.warning("createRowFromCsv: empty row skipped");
247
- }
248
- start = end;
249
- end = start;
250
- }
251
- return result;
252
- }
253
- static _createRowFromJsonObj(obj, datamodel) {
254
- // console.log("createRowFromJsonObj: obj=" + JSON.stringify(obj))
255
- const fields = datamodel.fields;
256
- let result = new Array(fields.length);
257
- let has_data = false;
258
- // console.log("createRowFromJsonObj: " + result)
259
- for (let i = 0; i < fields.length; i++) {
260
- const field = fields[i];
261
- let value = obj[field.name];
262
- // console.log("createRowFromJsonObj: key=" + field.name + ", val=" + val)
263
- if ((value === null) || (value === undefined)) { // must be checed first as null is an object
264
- result[i] = value;
265
- }
266
- else if (Array.isArray(value) || typeof value === "object") {
267
- result[i] = JSON.stringify(value).replaceAll("\"", "\\\""); // only single level deep objects, rest is handled as JSON-strings
268
- }
269
- else if (typeof value === "string") {
270
- value = index_js_1.OINOStr.decode(value, index_js_1.OINOContentType.json);
271
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof index_js_1.OINONumberDataField) && (datamodel.api.hashid)) {
272
- value = datamodel.api.hashid.decode(value);
273
- }
274
- result[i] = field.deserializeCell(value);
275
- }
276
- else {
277
- result[i] = value; // value types are passed as-is
278
- }
279
- has_data = has_data || (result[i] !== undefined);
280
- // console.log("createRowFromJsonObj: result["+i+"]=" + result[i])
281
- }
282
- // console.log("createRowFromJsonObj: " + result)
283
- if (has_data) {
284
- return result;
285
- }
286
- else {
287
- index_js_1.OINOLog.warning("createRowFromJsonObj: empty row skipped");
288
- return undefined;
289
- }
290
- }
291
- static _createRowFromJson(datamodel, data) {
292
- let result = [];
293
- // console.log("OINORowFactoryJson: data=" + data)
294
- const obj = JSON.parse(data);
295
- if (Array.isArray(obj)) {
296
- obj.forEach(row => {
297
- const data_row = this._createRowFromJsonObj(row, datamodel);
298
- if (data_row !== undefined) {
299
- result.push(data_row);
300
- }
301
- });
302
- }
303
- else {
304
- const data_row = this._createRowFromJsonObj(obj, datamodel);
305
- if (data_row !== undefined) {
306
- result.push(data_row);
307
- }
308
- }
309
- return result;
310
- }
311
- static _findMultipartBoundary(formData, multipartBoundary, start) {
312
- let n = formData.indexOf(multipartBoundary, start);
313
- if (n >= 0) {
314
- n += multipartBoundary.length + 2;
315
- }
316
- else {
317
- n = formData.length;
318
- }
319
- return n;
320
- }
321
- static _parseMultipartLine(data, start) {
322
- let line_end = data.indexOf('\r\n', start);
323
- if (line_end >= start) {
324
- return data.subarray(start, line_end).toString();
325
- }
326
- else {
327
- return '';
328
- }
329
- }
330
- static _multipartHeaderRegex = /Content-Disposition\: (form-data|file); name=\"([^\"]+)\"(; filename=.*)?/i;
331
- static _createRowFromFormdata(datamodel, data, multipartBoundary) {
332
- let result = [];
333
- try {
334
- const n = data.length;
335
- let start = this._findMultipartBoundary(data, multipartBoundary, 0);
336
- let end = this._findMultipartBoundary(data, multipartBoundary, start);
337
- // OINOLog.debug("createRowFromFormdata: enter", {start:start, end:end, multipartBoundary:multipartBoundary})
338
- const row = new Array(datamodel.fields.length);
339
- let has_data = false;
340
- while (end < n) {
341
- // OINOLog.debug("createRowFromFormdata: next block", {start:start, end:end, block:data.substring(start, end)})
342
- let block_ok = true;
343
- let l = this._parseMultipartLine(data, start);
344
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
345
- start += l.length + 2;
346
- const header_matches = OINOParser._multipartHeaderRegex.exec(l);
347
- if (!header_matches) {
348
- index_js_1.OINOLog.warning("OINODbFactory.createRowFromFormdata: unsupported block skipped!", { header_line: l });
349
- block_ok = false;
350
- }
351
- else {
352
- const field_name = header_matches[2];
353
- const is_file = header_matches[3] != null;
354
- let is_base64 = false;
355
- const field_index = datamodel.findFieldIndexByName(field_name);
356
- // OINOLog.debug("createRowFromFormdata: header", {field_name:field_name, field_index:field_index, is_file:is_file, is_base64:is_base64})
357
- if (field_index < 0) {
358
- index_js_1.OINOLog.warning("OINODbFactory.createRowFromFormdata: form field not found and skipped!", { field_name: field_name });
359
- block_ok = false;
360
- }
361
- else {
362
- const field = datamodel.fields[field_index];
363
- l = this._parseMultipartLine(data, start);
364
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
365
- while (block_ok && (l != '')) {
366
- if (l.startsWith('Content-Type:') && (l.indexOf('multipart/mixed') >= 0)) {
367
- index_js_1.OINOLog.warning("OINODbFactory.createRowFromFormdata: mixed multipart files not supported and skipped!", { header_line: l });
368
- block_ok = false;
369
- }
370
- else if (l.startsWith('Content-Transfer-Encoding:') && (l.indexOf('BASE64') >= 0)) {
371
- is_base64 = true;
372
- }
373
- start += l.length + 2;
374
- l = this._parseMultipartLine(data, start);
375
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
376
- }
377
- start += 2;
378
- if (!block_ok) {
379
- index_js_1.OINOLog.warning("OINODbFactory.createRowFromFormdata: invalid block skipped", { field_name: field_name });
380
- }
381
- else if (start + multipartBoundary.length + 2 >= end) {
382
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: null value", {field_name:field_name})
383
- row[field_index] = null;
384
- }
385
- else if (is_file) {
386
- if (is_base64) {
387
- const value = this._parseMultipartLine(data, start).trim();
388
- row[field_index] = field.deserializeCell(index_js_1.OINOStr.decode(value, index_js_1.OINOContentType.formdata));
389
- }
390
- else {
391
- const e = this._findMultipartBoundary(data, multipartBoundary, start);
392
- const value = data.subarray(start, e - 2);
393
- row[field_index] = value;
394
- }
395
- // console.log("OINODbFactory.createRowFromFormdata: file field", {field_name:field_name, value:row[field_index]})
396
- }
397
- else {
398
- let value = index_js_1.OINOStr.decode(this._parseMultipartLine(data, start).trim(), index_js_1.OINOContentType.formdata);
399
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: parse form field", {field_name:field_name, value:value})
400
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof index_js_1.OINONumberDataField) && (datamodel.api.hashid)) {
401
- value = datamodel.api.hashid.decode(value);
402
- }
403
- row[field_index] = field.deserializeCell(value);
404
- }
405
- has_data = has_data || (row[field_index] !== undefined);
406
- }
407
- }
408
- start = end;
409
- end = this._findMultipartBoundary(data, multipartBoundary, start);
410
- }
411
- // OINOLog.debug("createRowFromFormdata: next row", {row:row})
412
- if (has_data) {
413
- result.push(row);
414
- }
415
- else {
416
- index_js_1.OINOLog.warning("createRowFromFormdata: empty row skipped");
417
- }
418
- }
419
- catch (e) {
420
- index_js_1.OINOLog.error("createRowFromFormdata: error parsing formdata", { exception: e.message });
421
- }
422
- return result;
423
- }
424
- static _createRowFromUrlencoded(datamodel, data) {
425
- // OINOLog.debug("createRowFromUrlencoded: enter", {data:data})
426
- let result = [];
427
- const row = new Array(datamodel.fields.length);
428
- let has_data = false;
429
- const data_parts = data.trim().split('&');
430
- try {
431
- for (let i = 0; i < data_parts.length; i++) {
432
- const param_parts = data_parts[i].split('=');
433
- // OINOLog.debug("createRowFromUrlencoded: next param", {param_parts:param_parts})
434
- if (param_parts.length == 2) {
435
- const key = index_js_1.OINOStr.decodeUrlencode(param_parts[0]) || "";
436
- const field_index = datamodel.findFieldIndexByName(key);
437
- if (field_index < 0) {
438
- index_js_1.OINOLog.info("createRowFromUrlencoded: param field not found", { field: key });
439
- }
440
- else {
441
- const field = datamodel.fields[field_index];
442
- let value = index_js_1.OINOStr.decode(param_parts[1], index_js_1.OINOContentType.urlencode);
443
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof index_js_1.OINONumberDataField) && (datamodel.api.hashid)) {
444
- value = datamodel.api.hashid.decode(value);
445
- }
446
- row[field_index] = field.deserializeCell(value);
447
- has_data = has_data || (row[field_index] !== undefined);
448
- }
449
- }
450
- // const value = requestParams[]
451
- }
452
- if (has_data) {
453
- result.push(row);
454
- }
455
- else {
456
- index_js_1.OINOLog.warning("createRowFromUrlencoded: empty row skipped");
457
- }
458
- }
459
- catch (e) {
460
- index_js_1.OINOLog.error("createRowFromUrlencoded: error parsing urlencoded data", { exception: e.message });
461
- }
462
- // console.log("createRowFromUrlencoded: next row=" + row)
463
- return result;
464
- }
465
- }
466
- exports.OINOParser = OINOParser;
@@ -1,462 +0,0 @@
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
- import { OINOContentType, OINOStr, OINONumberDataField, OINOLog } from "../../db/src/index.js";
7
- /**
8
- * Static factory class for easily creating things based on data
9
- *
10
- */
11
- export class OINOParser {
12
- /**
13
- * Create data rows from request body based on the datamodel.
14
- *
15
- * @param datamodel datamodel of the api
16
- * @param data data as a string
17
- * @param requestParams parameters
18
- *
19
- */
20
- static createRows(datamodel, data, requestParams) {
21
- let result = [];
22
- if (typeof data == "string") {
23
- result = this.createRowsFromText(datamodel, data, requestParams);
24
- }
25
- else if (data instanceof Buffer) {
26
- result = this.createRowsFromBlob(datamodel, data, requestParams);
27
- }
28
- else if (typeof data == "object") {
29
- result = [this.createRowFromObject(datamodel, data)];
30
- }
31
- return result;
32
- }
33
- /**
34
- * Create data rows from request body based on the datamodel.
35
- *
36
- * @param datamodel datamodel of the api
37
- * @param data data as a string
38
- * @param requestParams parameters
39
- *
40
- */
41
- static createRowsFromText(datamodel, data, requestParams) {
42
- if ((requestParams.requestType == OINOContentType.json) || (requestParams.requestType == undefined)) {
43
- return this._createRowFromJson(datamodel, data);
44
- }
45
- else if (requestParams.requestType == OINOContentType.csv) {
46
- return this._createRowFromCsv(datamodel, data);
47
- }
48
- else if (requestParams.requestType == OINOContentType.formdata) {
49
- return this._createRowFromFormdata(datamodel, Buffer.from(data, "utf8"), requestParams.multipartBoundary || "");
50
- }
51
- else if (requestParams.requestType == OINOContentType.urlencode) {
52
- return this._createRowFromUrlencoded(datamodel, data);
53
- }
54
- else if (requestParams.requestType == OINOContentType.html) {
55
- OINOLog.error("HTML can't be used as an input content type!", { contentType: OINOContentType.html });
56
- return [];
57
- }
58
- else {
59
- OINOLog.error("Unrecognized input content type!", { contentType: requestParams.requestType });
60
- return [];
61
- }
62
- }
63
- /**
64
- * Create data rows from request body based on the datamodel.
65
- *
66
- * @param datamodel datamodel of the api
67
- * @param data data as an Buffer
68
- * @param requestParams parameters
69
- *
70
- */
71
- static createRowsFromBlob(datamodel, data, requestParams) {
72
- if ((requestParams.requestType == OINOContentType.json) || (requestParams.requestType == undefined)) {
73
- return this._createRowFromJson(datamodel, data.toString()); // JSON is always a string
74
- }
75
- else if (requestParams.requestType == OINOContentType.csv) {
76
- return this._createRowFromCsv(datamodel, data.toString()); // binary data has to be base64 encoded so it's a string
77
- }
78
- else if (requestParams.requestType == OINOContentType.formdata) {
79
- return this._createRowFromFormdata(datamodel, data, requestParams.multipartBoundary || "");
80
- }
81
- else if (requestParams.requestType == OINOContentType.urlencode) {
82
- return this._createRowFromUrlencoded(datamodel, data.toString()); // data is urlencoded so it's a string
83
- }
84
- else if (requestParams.requestType == OINOContentType.html) {
85
- OINOLog.error("HTML can't be used as an input content type!", { contentType: OINOContentType.html });
86
- return [];
87
- }
88
- else {
89
- OINOLog.error("Unrecognized input content type!", { contentType: requestParams.requestType });
90
- return [];
91
- }
92
- }
93
- /**
94
- * Create one data row from javascript object based on the datamodel.
95
- * NOTE! Data assumed to be unserialized i.e. of the native type (string, number, boolean, Buffer)
96
- *
97
- * @param datamodel datamodel of the api
98
- * @param data data as javascript object
99
- *
100
- */
101
- static createRowFromObject(datamodel, data) {
102
- const fields = datamodel.fields;
103
- let result = new Array(fields.length);
104
- for (let i = 0; i < fields.length; i++) {
105
- result[i] = data[fields[i].name];
106
- }
107
- return result;
108
- }
109
- static _findCsvLineEnd(csvData, start) {
110
- const n = csvData.length;
111
- if (start >= n) {
112
- return start;
113
- }
114
- let end = start;
115
- let quote_open = false;
116
- while (end < n) {
117
- if (csvData[end] == "\"") {
118
- if (!quote_open) {
119
- quote_open = true;
120
- }
121
- else if ((end < n - 1) && (csvData[end + 1] == "\"")) {
122
- end++;
123
- }
124
- else {
125
- quote_open = false;
126
- }
127
- }
128
- else if ((!quote_open) && (csvData[end] == "\r")) {
129
- return end;
130
- }
131
- end++;
132
- }
133
- return n;
134
- }
135
- static _parseCsvLine(csvLine) {
136
- let result = [];
137
- const n = csvLine.length;
138
- let start = 0;
139
- let end = 0;
140
- let quote_open = false;
141
- let has_quotes = false;
142
- let has_escaped_quotes = false;
143
- let found_field = false;
144
- while (end < n) {
145
- if (csvLine[end] == "\"") {
146
- if (!quote_open) {
147
- quote_open = true;
148
- }
149
- else if ((end < n - 1) && (csvLine[end + 1] == "\"")) {
150
- end++;
151
- has_escaped_quotes = true;
152
- }
153
- else {
154
- has_quotes = true;
155
- quote_open = false;
156
- }
157
- }
158
- if ((!quote_open) && ((end == n - 1) || (csvLine[end] == ","))) {
159
- found_field = true;
160
- if (end == n - 1) {
161
- end++;
162
- }
163
- }
164
- if (found_field) {
165
- // console.log("OINODB_csvParseLine: next field=" + csvLine.substring(start,end) + ", start="+start+", end="+end)
166
- let field_str;
167
- if (has_quotes) {
168
- field_str = csvLine.substring(start + 1, end - 1);
169
- }
170
- else if (start == end) {
171
- field_str = undefined;
172
- }
173
- else {
174
- field_str = csvLine.substring(start, end);
175
- if (field_str == "null") {
176
- field_str = null;
177
- }
178
- }
179
- result.push(field_str);
180
- has_quotes = false;
181
- has_escaped_quotes = true;
182
- found_field = false;
183
- start = end + 1;
184
- }
185
- end++;
186
- }
187
- return result;
188
- }
189
- static _createRowFromCsv(datamodel, data) {
190
- let result = [];
191
- const n = data.length;
192
- let start = 0;
193
- let end = this._findCsvLineEnd(data, start);
194
- const header_str = data.substring(start, end);
195
- const headers = this._parseCsvLine(header_str);
196
- let field_to_header_mapping = new Array(datamodel.fields.length);
197
- let headers_found = false;
198
- for (let i = 0; i < field_to_header_mapping.length; i++) {
199
- field_to_header_mapping[i] = headers.indexOf(datamodel.fields[i].name);
200
- headers_found = headers_found || (field_to_header_mapping[i] >= 0);
201
- }
202
- // OINOLog.debug("createRowFromCsv", {headers:headers, field_to_header_mapping:field_to_header_mapping})
203
- if (!headers_found) {
204
- return result;
205
- }
206
- start = end + 1;
207
- end = start;
208
- while (end < n) {
209
- while ((start < n) && ((data[start] == "\r") || (data[start] == "\n"))) {
210
- start++;
211
- }
212
- if (start >= n) {
213
- return result;
214
- }
215
- end = this._findCsvLineEnd(data, start);
216
- const row_data = this._parseCsvLine(data.substring(start, end));
217
- const row = new Array(field_to_header_mapping.length);
218
- let has_data = false;
219
- for (let i = 0; i < datamodel.fields.length; i++) {
220
- const field = datamodel.fields[i];
221
- let j = field_to_header_mapping[i];
222
- let value = row_data[j];
223
- if ((value === undefined) || (value === null)) { // null/undefined-decoding built into the parser
224
- row[i] = value;
225
- }
226
- else if ((j >= 0) && (j < row_data.length)) {
227
- value = OINOStr.decode(value, OINOContentType.csv);
228
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
229
- value = datamodel.api.hashid.decode(value);
230
- }
231
- row[i] = field.deserializeCell(value);
232
- }
233
- else {
234
- row[i] = undefined;
235
- }
236
- has_data = has_data || (row[i] !== undefined);
237
- }
238
- // console.log("createRowFromCsv: next row=" + row)
239
- if (has_data) {
240
- result.push(row);
241
- }
242
- else {
243
- OINOLog.warning("createRowFromCsv: empty row skipped");
244
- }
245
- start = end;
246
- end = start;
247
- }
248
- return result;
249
- }
250
- static _createRowFromJsonObj(obj, datamodel) {
251
- // console.log("createRowFromJsonObj: obj=" + JSON.stringify(obj))
252
- const fields = datamodel.fields;
253
- let result = new Array(fields.length);
254
- let has_data = false;
255
- // console.log("createRowFromJsonObj: " + result)
256
- for (let i = 0; i < fields.length; i++) {
257
- const field = fields[i];
258
- let value = obj[field.name];
259
- // console.log("createRowFromJsonObj: key=" + field.name + ", val=" + val)
260
- if ((value === null) || (value === undefined)) { // must be checed first as null is an object
261
- result[i] = value;
262
- }
263
- else if (Array.isArray(value) || typeof value === "object") {
264
- result[i] = JSON.stringify(value).replaceAll("\"", "\\\""); // only single level deep objects, rest is handled as JSON-strings
265
- }
266
- else if (typeof value === "string") {
267
- value = OINOStr.decode(value, OINOContentType.json);
268
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
269
- value = datamodel.api.hashid.decode(value);
270
- }
271
- result[i] = field.deserializeCell(value);
272
- }
273
- else {
274
- result[i] = value; // value types are passed as-is
275
- }
276
- has_data = has_data || (result[i] !== undefined);
277
- // console.log("createRowFromJsonObj: result["+i+"]=" + result[i])
278
- }
279
- // console.log("createRowFromJsonObj: " + result)
280
- if (has_data) {
281
- return result;
282
- }
283
- else {
284
- OINOLog.warning("createRowFromJsonObj: empty row skipped");
285
- return undefined;
286
- }
287
- }
288
- static _createRowFromJson(datamodel, data) {
289
- let result = [];
290
- // console.log("OINORowFactoryJson: data=" + data)
291
- const obj = JSON.parse(data);
292
- if (Array.isArray(obj)) {
293
- obj.forEach(row => {
294
- const data_row = this._createRowFromJsonObj(row, datamodel);
295
- if (data_row !== undefined) {
296
- result.push(data_row);
297
- }
298
- });
299
- }
300
- else {
301
- const data_row = this._createRowFromJsonObj(obj, datamodel);
302
- if (data_row !== undefined) {
303
- result.push(data_row);
304
- }
305
- }
306
- return result;
307
- }
308
- static _findMultipartBoundary(formData, multipartBoundary, start) {
309
- let n = formData.indexOf(multipartBoundary, start);
310
- if (n >= 0) {
311
- n += multipartBoundary.length + 2;
312
- }
313
- else {
314
- n = formData.length;
315
- }
316
- return n;
317
- }
318
- static _parseMultipartLine(data, start) {
319
- let line_end = data.indexOf('\r\n', start);
320
- if (line_end >= start) {
321
- return data.subarray(start, line_end).toString();
322
- }
323
- else {
324
- return '';
325
- }
326
- }
327
- static _multipartHeaderRegex = /Content-Disposition\: (form-data|file); name=\"([^\"]+)\"(; filename=.*)?/i;
328
- static _createRowFromFormdata(datamodel, data, multipartBoundary) {
329
- let result = [];
330
- try {
331
- const n = data.length;
332
- let start = this._findMultipartBoundary(data, multipartBoundary, 0);
333
- let end = this._findMultipartBoundary(data, multipartBoundary, start);
334
- // OINOLog.debug("createRowFromFormdata: enter", {start:start, end:end, multipartBoundary:multipartBoundary})
335
- const row = new Array(datamodel.fields.length);
336
- let has_data = false;
337
- while (end < n) {
338
- // OINOLog.debug("createRowFromFormdata: next block", {start:start, end:end, block:data.substring(start, end)})
339
- let block_ok = true;
340
- let l = this._parseMultipartLine(data, start);
341
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
342
- start += l.length + 2;
343
- const header_matches = OINOParser._multipartHeaderRegex.exec(l);
344
- if (!header_matches) {
345
- OINOLog.warning("OINODbFactory.createRowFromFormdata: unsupported block skipped!", { header_line: l });
346
- block_ok = false;
347
- }
348
- else {
349
- const field_name = header_matches[2];
350
- const is_file = header_matches[3] != null;
351
- let is_base64 = false;
352
- const field_index = datamodel.findFieldIndexByName(field_name);
353
- // OINOLog.debug("createRowFromFormdata: header", {field_name:field_name, field_index:field_index, is_file:is_file, is_base64:is_base64})
354
- if (field_index < 0) {
355
- OINOLog.warning("OINODbFactory.createRowFromFormdata: form field not found and skipped!", { field_name: field_name });
356
- block_ok = false;
357
- }
358
- else {
359
- const field = datamodel.fields[field_index];
360
- l = this._parseMultipartLine(data, start);
361
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
362
- while (block_ok && (l != '')) {
363
- if (l.startsWith('Content-Type:') && (l.indexOf('multipart/mixed') >= 0)) {
364
- OINOLog.warning("OINODbFactory.createRowFromFormdata: mixed multipart files not supported and skipped!", { header_line: l });
365
- block_ok = false;
366
- }
367
- else if (l.startsWith('Content-Transfer-Encoding:') && (l.indexOf('BASE64') >= 0)) {
368
- is_base64 = true;
369
- }
370
- start += l.length + 2;
371
- l = this._parseMultipartLine(data, start);
372
- // OINOLog.debug("createRowFromFormdata: next line", {start:start, end:end, line:l})
373
- }
374
- start += 2;
375
- if (!block_ok) {
376
- OINOLog.warning("OINODbFactory.createRowFromFormdata: invalid block skipped", { field_name: field_name });
377
- }
378
- else if (start + multipartBoundary.length + 2 >= end) {
379
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: null value", {field_name:field_name})
380
- row[field_index] = null;
381
- }
382
- else if (is_file) {
383
- if (is_base64) {
384
- const value = this._parseMultipartLine(data, start).trim();
385
- row[field_index] = field.deserializeCell(OINOStr.decode(value, OINOContentType.formdata));
386
- }
387
- else {
388
- const e = this._findMultipartBoundary(data, multipartBoundary, start);
389
- const value = data.subarray(start, e - 2);
390
- row[field_index] = value;
391
- }
392
- // console.log("OINODbFactory.createRowFromFormdata: file field", {field_name:field_name, value:row[field_index]})
393
- }
394
- else {
395
- let value = OINOStr.decode(this._parseMultipartLine(data, start).trim(), OINOContentType.formdata);
396
- // OINOLog.debug("OINODbFactory.createRowFromFormdata: parse form field", {field_name:field_name, value:value})
397
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
398
- value = datamodel.api.hashid.decode(value);
399
- }
400
- row[field_index] = field.deserializeCell(value);
401
- }
402
- has_data = has_data || (row[field_index] !== undefined);
403
- }
404
- }
405
- start = end;
406
- end = this._findMultipartBoundary(data, multipartBoundary, start);
407
- }
408
- // OINOLog.debug("createRowFromFormdata: next row", {row:row})
409
- if (has_data) {
410
- result.push(row);
411
- }
412
- else {
413
- OINOLog.warning("createRowFromFormdata: empty row skipped");
414
- }
415
- }
416
- catch (e) {
417
- OINOLog.error("createRowFromFormdata: error parsing formdata", { exception: e.message });
418
- }
419
- return result;
420
- }
421
- static _createRowFromUrlencoded(datamodel, data) {
422
- // OINOLog.debug("createRowFromUrlencoded: enter", {data:data})
423
- let result = [];
424
- const row = new Array(datamodel.fields.length);
425
- let has_data = false;
426
- const data_parts = data.trim().split('&');
427
- try {
428
- for (let i = 0; i < data_parts.length; i++) {
429
- const param_parts = data_parts[i].split('=');
430
- // OINOLog.debug("createRowFromUrlencoded: next param", {param_parts:param_parts})
431
- if (param_parts.length == 2) {
432
- const key = OINOStr.decodeUrlencode(param_parts[0]) || "";
433
- const field_index = datamodel.findFieldIndexByName(key);
434
- if (field_index < 0) {
435
- OINOLog.info("createRowFromUrlencoded: param field not found", { field: key });
436
- }
437
- else {
438
- const field = datamodel.fields[field_index];
439
- let value = OINOStr.decode(param_parts[1], OINOContentType.urlencode);
440
- if (value && (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) && (field instanceof OINONumberDataField) && (datamodel.api.hashid)) {
441
- value = datamodel.api.hashid.decode(value);
442
- }
443
- row[field_index] = field.deserializeCell(value);
444
- has_data = has_data || (row[field_index] !== undefined);
445
- }
446
- }
447
- // const value = requestParams[]
448
- }
449
- if (has_data) {
450
- result.push(row);
451
- }
452
- else {
453
- OINOLog.warning("createRowFromUrlencoded: empty row skipped");
454
- }
455
- }
456
- catch (e) {
457
- OINOLog.error("createRowFromUrlencoded: error parsing urlencoded data", { exception: e.message });
458
- }
459
- // console.log("createRowFromUrlencoded: next row=" + row)
460
- return result;
461
- }
462
- }
@@ -1,53 +0,0 @@
1
- import { OINODbDataModel, OINODataRow, OINODbApiRequestParams } from "../../db/src/index.js";
2
- /**
3
- * Static factory class for easily creating things based on data
4
- *
5
- */
6
- export declare class OINOParser {
7
- /**
8
- * Create data rows from request body based on the datamodel.
9
- *
10
- * @param datamodel datamodel of the api
11
- * @param data data as a string
12
- * @param requestParams parameters
13
- *
14
- */
15
- static createRows(datamodel: OINODbDataModel, data: string | Buffer | object, requestParams: OINODbApiRequestParams): OINODataRow[];
16
- /**
17
- * Create data rows from request body based on the datamodel.
18
- *
19
- * @param datamodel datamodel of the api
20
- * @param data data as a string
21
- * @param requestParams parameters
22
- *
23
- */
24
- static createRowsFromText(datamodel: OINODbDataModel, data: string, requestParams: OINODbApiRequestParams): OINODataRow[];
25
- /**
26
- * Create data rows from request body based on the datamodel.
27
- *
28
- * @param datamodel datamodel of the api
29
- * @param data data as an Buffer
30
- * @param requestParams parameters
31
- *
32
- */
33
- static createRowsFromBlob(datamodel: OINODbDataModel, data: Buffer, requestParams: OINODbApiRequestParams): OINODataRow[];
34
- /**
35
- * Create one data row from javascript object based on the datamodel.
36
- * NOTE! Data assumed to be unserialized i.e. of the native type (string, number, boolean, Buffer)
37
- *
38
- * @param datamodel datamodel of the api
39
- * @param data data as javascript object
40
- *
41
- */
42
- static createRowFromObject(datamodel: OINODbDataModel, data: any): OINODataRow;
43
- private static _findCsvLineEnd;
44
- private static _parseCsvLine;
45
- private static _createRowFromCsv;
46
- private static _createRowFromJsonObj;
47
- private static _createRowFromJson;
48
- private static _findMultipartBoundary;
49
- private static _parseMultipartLine;
50
- private static _multipartHeaderRegex;
51
- private static _createRowFromFormdata;
52
- private static _createRowFromUrlencoded;
53
- }