@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.
- package/README.md +183 -0
- package/dist/cjs/OINOApi.js +322 -0
- package/dist/cjs/OINOBenchmark.js +3 -4
- package/dist/cjs/OINOConfig.js +104 -0
- package/dist/cjs/OINOConstants.js +42 -0
- package/dist/cjs/OINODataField.js +346 -0
- package/dist/cjs/OINODataModel.js +182 -0
- package/dist/cjs/OINODataSource.js +165 -0
- package/dist/cjs/OINOFormatter.js +6 -5
- package/dist/cjs/OINOHtmlTemplate.js +21 -18
- package/dist/cjs/OINOModelSet.js +333 -0
- package/dist/cjs/OINOParser.js +448 -0
- package/dist/cjs/OINOQueryParams.js +434 -0
- package/dist/cjs/OINORequest.js +21 -13
- package/dist/cjs/OINOResult.js +13 -12
- package/dist/cjs/OINOStr.js +11 -11
- package/dist/cjs/OINOSwagger.js +205 -0
- package/dist/cjs/index.js +57 -39
- package/dist/esm/OINOApi.js +315 -0
- package/dist/esm/OINOBenchmark.js +3 -4
- package/dist/esm/OINOConfig.js +100 -0
- package/dist/esm/OINOConstants.js +39 -0
- package/dist/esm/OINODataField.js +337 -0
- package/dist/esm/OINODataModel.js +178 -0
- package/dist/esm/OINODataSource.js +159 -0
- package/dist/esm/OINOFormatter.js +2 -1
- package/dist/esm/OINOHtmlTemplate.js +4 -1
- package/dist/esm/OINOModelSet.js +329 -0
- package/dist/esm/OINOParser.js +444 -0
- package/dist/esm/OINOQueryParams.js +426 -0
- package/dist/esm/OINORequest.js +9 -1
- package/dist/esm/OINOResult.js +2 -1
- package/dist/esm/OINOStr.js +1 -1
- package/dist/esm/OINOSwagger.js +201 -0
- package/dist/esm/index.js +14 -32
- package/dist/types/OINOApi.d.ts +191 -0
- package/dist/types/OINOBenchmark.d.ts +1 -1
- package/dist/types/OINOConfig.d.ts +63 -0
- package/dist/types/OINOConstants.d.ts +51 -0
- package/dist/types/OINODataField.d.ts +209 -0
- package/dist/types/OINODataModel.d.ts +78 -0
- package/dist/types/OINODataSource.d.ts +184 -0
- package/dist/types/OINOHtmlTemplate.d.ts +1 -1
- package/dist/types/OINOModelSet.d.ts +64 -0
- package/dist/types/OINOParser.d.ts +42 -0
- package/dist/types/OINOQueryParams.d.ts +270 -0
- package/dist/types/OINORequest.d.ts +4 -1
- package/dist/types/OINOResult.d.ts +1 -1
- package/dist/types/OINOStr.d.ts +1 -1
- package/dist/types/OINOSwagger.d.ts +25 -0
- package/dist/types/index.d.ts +14 -31
- package/package.json +32 -32
- package/src/OINOApi.ts +429 -0
- package/src/OINOBenchmark.ts +323 -324
- package/src/OINOConfig.ts +113 -0
- package/src/OINOConstants.ts +59 -0
- package/src/OINODataField.ts +371 -0
- package/src/OINODataModel.ts +187 -0
- package/src/OINODataSource.ts +280 -0
- package/src/OINOFormatter.ts +166 -165
- package/src/OINOHeaders.ts +51 -51
- package/src/OINOHtmlTemplate.test.ts +114 -114
- package/src/OINOHtmlTemplate.ts +225 -222
- package/src/OINOLog.ts +292 -292
- package/src/OINOModelSet.ts +359 -0
- package/src/OINOParser.ts +441 -0
- package/src/OINOQueryParams.ts +449 -0
- package/src/OINORequest.ts +204 -196
- package/src/OINOResult.ts +331 -330
- package/src/OINOStr.ts +254 -254
- package/src/OINOSwagger.ts +213 -0
- package/src/index.ts +18 -38
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { OINO_EMPTY_ROW, OINO_ERROR_PREFIX } from "./OINOConstants.js";
|
|
2
|
+
import { OINOResult } from "./OINOResult.js";
|
|
3
|
+
/**
|
|
4
|
+
* Base class for database abstraction, implementing methods for connecting, making queries and parsing/formatting data
|
|
5
|
+
* between SQL and serialization formats.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export class OINODataSource {
|
|
9
|
+
isConnected = false;
|
|
10
|
+
isValidated = false;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Base class for SQL results that can be asynchronously iterated (but
|
|
14
|
+
* not necessarity rewinded). Idea is to handle database specific mechanisms
|
|
15
|
+
* for returning and formatting conventions in the database specific
|
|
16
|
+
* implementation. Data might be in memory or streamed in chunks and
|
|
17
|
+
* `OINODataSet` will serve it out consistently.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
export class OINODataSet extends OINOResult {
|
|
21
|
+
_data;
|
|
22
|
+
/** Error messages */
|
|
23
|
+
messages;
|
|
24
|
+
/**
|
|
25
|
+
* Constructor for `OINODataSet`.
|
|
26
|
+
*
|
|
27
|
+
* @param data internal database specific data type (constructor will throw if invalid)
|
|
28
|
+
* @param messages error messages from SQL-query
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
constructor(data, messages = []) {
|
|
32
|
+
super();
|
|
33
|
+
this._data = data;
|
|
34
|
+
this.messages = messages;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Checks if the messages contain errors.
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
hasErrors() {
|
|
41
|
+
for (let i = 0; i < this.messages.length; i++) {
|
|
42
|
+
if (this.messages[i].startsWith(OINO_ERROR_PREFIX)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Finds the first error message that occured
|
|
50
|
+
*
|
|
51
|
+
*/
|
|
52
|
+
getFirstError() {
|
|
53
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
54
|
+
if (this.messages[i].startsWith(OINO_ERROR_PREFIX)) {
|
|
55
|
+
return this.messages[i];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Finds the last error message that occured
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
getLastError() {
|
|
65
|
+
for (let i = 0; i < this.messages.length; i++) {
|
|
66
|
+
if (this.messages[i].startsWith(OINO_ERROR_PREFIX)) {
|
|
67
|
+
return this.messages[i];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generic in memory implementation of a data set where data is an array of rows. Used
|
|
75
|
+
* by BunSqlite and automated testing. Can be rewinded.
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
78
|
+
export class OINOMemoryDataset extends OINODataSet {
|
|
79
|
+
_rows;
|
|
80
|
+
_currentRow;
|
|
81
|
+
_eof;
|
|
82
|
+
/**
|
|
83
|
+
* Constructor of `OINOMemoryDataset`.
|
|
84
|
+
*
|
|
85
|
+
* @param data data as OINODataRow[] (constructor will throw if invalid)
|
|
86
|
+
* @param errors error messages from SQL-query
|
|
87
|
+
*
|
|
88
|
+
*/
|
|
89
|
+
constructor(data, errors = []) {
|
|
90
|
+
super(data, errors);
|
|
91
|
+
if ((data == null) || !(Array.isArray(data))) {
|
|
92
|
+
throw new Error(OINO_ERROR_PREFIX + ": Data needs to be compatible with OINORow[]!"); // TODO: maybe check all rows
|
|
93
|
+
}
|
|
94
|
+
this._rows = data;
|
|
95
|
+
if (this.isEmpty()) {
|
|
96
|
+
this._currentRow = -1;
|
|
97
|
+
this._eof = true;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this._currentRow = 0;
|
|
101
|
+
this._eof = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Is data set empty.
|
|
106
|
+
*
|
|
107
|
+
*/
|
|
108
|
+
isEmpty() {
|
|
109
|
+
return (this._rows.length == 0);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Is there no more content, i.e. either dataset is empty or we have moved beyond last line
|
|
113
|
+
*
|
|
114
|
+
*/
|
|
115
|
+
isEof() {
|
|
116
|
+
return (this._eof);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
async next() {
|
|
123
|
+
if (this._currentRow < this._rows.length - 1) {
|
|
124
|
+
this._currentRow = this._currentRow + 1;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this._eof = true;
|
|
128
|
+
}
|
|
129
|
+
return Promise.resolve(!this._eof);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Gets current row of data.
|
|
133
|
+
*
|
|
134
|
+
*/
|
|
135
|
+
getRow() {
|
|
136
|
+
if ((this._currentRow >= 0) && (this._currentRow < this._rows.length)) {
|
|
137
|
+
return this._rows[this._currentRow];
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
return OINO_EMPTY_ROW;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Gets all rows of data.
|
|
145
|
+
*
|
|
146
|
+
*/
|
|
147
|
+
async getAllRows() {
|
|
148
|
+
return this._rows; // at the moment theres no result streaming, so we can just return the rows
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Rewinds data set to the first row, returns !isEof().
|
|
152
|
+
*
|
|
153
|
+
*/
|
|
154
|
+
first() {
|
|
155
|
+
this._currentRow = 0;
|
|
156
|
+
this._eof = this._rows.length == 0;
|
|
157
|
+
return !this._eof;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
4
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
5
|
*/
|
|
6
|
-
import { OINO_ERROR_PREFIX
|
|
6
|
+
import { OINO_ERROR_PREFIX } from "./OINOConstants.js";
|
|
7
|
+
import { OINOLog } from "./OINOLog.js";
|
|
7
8
|
/**
|
|
8
9
|
* Class for formatting strings and values.
|
|
9
10
|
*
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OINOContentType, OINO_ERROR_PREFIX, OINO_WARNING_PREFIX, OINO_INFO_PREFIX, OINO_DEBUG_PREFIX } from "./OINOConstants.js";
|
|
2
|
+
import { OINOStr } from "./OINOStr.js";
|
|
3
|
+
import { OINOHttpResult } from "./OINOResult.js";
|
|
4
|
+
import { OINOBenchmark } from "./OINOBenchmark.js";
|
|
2
5
|
import { OINO_EMPTY_FORMATTER, OINOFormatter } from "./OINOFormatter";
|
|
3
6
|
/**
|
|
4
7
|
* Class for rendering HTML from data.
|
|
@@ -0,0 +1,329 @@
|
|
|
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 } from "./OINOConstants.js";
|
|
7
|
+
import { OINOConfig } from "./OINOConfig.js";
|
|
8
|
+
import { OINOStr } from "./OINOStr.js";
|
|
9
|
+
import { OINOLog } from "./OINOLog.js";
|
|
10
|
+
import { OINOBlobDataField, OINONumberDataField, OINOBooleanDataField } from "./OINODataField.js";
|
|
11
|
+
/**
|
|
12
|
+
* Class for dataset based on a data model that can be serialized to
|
|
13
|
+
* a supported format:
|
|
14
|
+
* - JSON (application/json)
|
|
15
|
+
* - CSV (text/csv)
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export class OINOModelSet {
|
|
19
|
+
/** Reference to datamodel */
|
|
20
|
+
datamodel;
|
|
21
|
+
/** Reference to data set */
|
|
22
|
+
dataset;
|
|
23
|
+
/** SQL parameters */
|
|
24
|
+
queryParams;
|
|
25
|
+
/** Collection of errors */
|
|
26
|
+
errors;
|
|
27
|
+
/**
|
|
28
|
+
* Constructor for `OINOModelSet`.
|
|
29
|
+
*
|
|
30
|
+
* @param datamodel data model
|
|
31
|
+
* @param dataset data set
|
|
32
|
+
* @param queryParams SQL parameters
|
|
33
|
+
*/
|
|
34
|
+
constructor(datamodel, dataset, queryParams) {
|
|
35
|
+
this.datamodel = datamodel;
|
|
36
|
+
this.dataset = dataset;
|
|
37
|
+
this.queryParams = queryParams;
|
|
38
|
+
this.errors = this.dataset.messages;
|
|
39
|
+
}
|
|
40
|
+
_encodeAndHashFieldValue(field, value, contentType, primaryKeyValues, rowIdSeed) {
|
|
41
|
+
let result;
|
|
42
|
+
if (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) {
|
|
43
|
+
if (value && (field instanceof OINONumberDataField) && (this.datamodel.api.hashid) && ((this.queryParams?.aggregate === undefined) || (this.queryParams.aggregate.isAggregated(field.name) == false))) {
|
|
44
|
+
value = this.datamodel.api.hashid.encode(value, rowIdSeed);
|
|
45
|
+
}
|
|
46
|
+
if (field.fieldParams.isPrimaryKey) {
|
|
47
|
+
primaryKeyValues.push(value || "");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
result = OINOStr.encode(value, contentType);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
_writeRowJson(row) {
|
|
54
|
+
// console.log("OINOModelSet._writeRowJson: row=" + row)
|
|
55
|
+
const model = this.datamodel;
|
|
56
|
+
const fields = model.fields;
|
|
57
|
+
let row_id_seed = model.getRowPrimarykeyValues(row).join(' ');
|
|
58
|
+
let primary_key_values = [];
|
|
59
|
+
let json_row = "";
|
|
60
|
+
for (let i = 0; i < fields.length; i++) {
|
|
61
|
+
const f = fields[i];
|
|
62
|
+
if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
let value = f.serializeCell(row[i]);
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
// skip undefined values
|
|
68
|
+
}
|
|
69
|
+
else if (value === null) {
|
|
70
|
+
json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":null";
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
let is_hashed = (f.fieldParams.isPrimaryKey || f.fieldParams.isForeignKey) && (f instanceof OINONumberDataField) && (this.datamodel.api.hashid != null);
|
|
74
|
+
let is_value = (f instanceof OINOBooleanDataField) || ((f instanceof OINONumberDataField) && !is_hashed);
|
|
75
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.json, primary_key_values, f.name + " " + row_id_seed);
|
|
76
|
+
if (is_value) {
|
|
77
|
+
value = value.substring(1, value.length - 1);
|
|
78
|
+
}
|
|
79
|
+
json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":" + value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
json_row = OINOStr.encode(OINOConfig.OINO_ID_FIELD, OINOContentType.json) + ":" + OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.json) + json_row;
|
|
83
|
+
return "{" + json_row + "}";
|
|
84
|
+
}
|
|
85
|
+
async _writeStringJson() {
|
|
86
|
+
let result = "";
|
|
87
|
+
while (!this.dataset.isEof()) {
|
|
88
|
+
if (result != "") {
|
|
89
|
+
result += ",\r\n";
|
|
90
|
+
}
|
|
91
|
+
const row = this.dataset.getRow();
|
|
92
|
+
result += this._writeRowJson(row);
|
|
93
|
+
await this.dataset.next();
|
|
94
|
+
}
|
|
95
|
+
result = "[\r\n" + result + "\r\n]";
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
_writeHeaderCsv() {
|
|
99
|
+
const model = this.datamodel;
|
|
100
|
+
const fields = model.fields;
|
|
101
|
+
let csv_header = "\"" + OINOConfig.OINO_ID_FIELD + "\"";
|
|
102
|
+
for (let i = 0; i < fields.length; i++) {
|
|
103
|
+
if ((this.queryParams?.select?.isSelected(fields[i].name) === false) && (fields[i].fieldParams.isPrimaryKey == false)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
csv_header += ",\"" + fields[i].name + "\"";
|
|
107
|
+
}
|
|
108
|
+
return csv_header;
|
|
109
|
+
}
|
|
110
|
+
_writeRowCsv(row) {
|
|
111
|
+
const model = this.datamodel;
|
|
112
|
+
const fields = model.fields;
|
|
113
|
+
let row_id_seed = model.getRowPrimarykeyValues(row).join(' ');
|
|
114
|
+
let primary_key_values = [];
|
|
115
|
+
let csv_row = "";
|
|
116
|
+
for (let i = 0; i < fields.length; i++) {
|
|
117
|
+
const f = fields[i];
|
|
118
|
+
if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
let value = f.serializeCell(row[i]);
|
|
122
|
+
if (value == null) {
|
|
123
|
+
csv_row += "," + OINOStr.encode(value, OINOContentType.csv); // either null or undefined
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.csv, primary_key_values, f.name + " " + row_id_seed);
|
|
127
|
+
csv_row += "," + value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
csv_row = OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.csv) + csv_row;
|
|
131
|
+
return csv_row;
|
|
132
|
+
}
|
|
133
|
+
async _writeStringCsv() {
|
|
134
|
+
let result = this._writeHeaderCsv();
|
|
135
|
+
while (!this.dataset.isEof()) {
|
|
136
|
+
if (result != "") {
|
|
137
|
+
result += "\r\n";
|
|
138
|
+
}
|
|
139
|
+
const row = this.dataset.getRow();
|
|
140
|
+
result += this._writeRowCsv(row);
|
|
141
|
+
await this.dataset.next();
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
_writeRowFormdataParameterBlock(blockName, blockValue, multipartBoundary) {
|
|
146
|
+
if (blockValue === null) {
|
|
147
|
+
return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n";
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n" + blockValue + "\r\n";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
_writeRowFormdataFileBlock(blockName, blockValue, multipartBoundary) {
|
|
154
|
+
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";
|
|
155
|
+
}
|
|
156
|
+
_writeRowFormdata(row) {
|
|
157
|
+
const multipart_boundary = "---------OINOMultipartBoundary35424568"; // this method is just used for test data generation and we want it to be static
|
|
158
|
+
const model = this.datamodel;
|
|
159
|
+
const fields = model.fields;
|
|
160
|
+
let row_id_seed = model.getRowPrimarykeyValues(row).join(' ');
|
|
161
|
+
let primary_key_values = [];
|
|
162
|
+
let result = "";
|
|
163
|
+
for (let i = 0; i < fields.length; i++) {
|
|
164
|
+
const f = fields[i];
|
|
165
|
+
if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
let value = f.serializeCell(row[i]);
|
|
169
|
+
let formdata_block = "";
|
|
170
|
+
let is_file = (f instanceof OINOBlobDataField);
|
|
171
|
+
if (value === undefined) {
|
|
172
|
+
OINOLog.info("@oino-ts/db", "OINOModelSet", "_writeRowFormdata", "Undefined value skipped", { field_name: f.name });
|
|
173
|
+
}
|
|
174
|
+
else if (value === null) {
|
|
175
|
+
formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, null, multipart_boundary);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.formdata, primary_key_values, f.name + " " + row_id_seed);
|
|
179
|
+
if (is_file) {
|
|
180
|
+
formdata_block = this._writeRowFormdataFileBlock(f.name, value, multipart_boundary);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, value, multipart_boundary);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
result += formdata_block;
|
|
187
|
+
}
|
|
188
|
+
result = this._writeRowFormdataParameterBlock(OINOConfig.OINO_ID_FIELD, OINOConfig.printOINOId(primary_key_values), multipart_boundary) + result;
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
_writeStringFormdata() {
|
|
192
|
+
const row = this.dataset.getRow();
|
|
193
|
+
let result = this._writeRowFormdata(row);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
_writeRowUrlencode(row) {
|
|
197
|
+
const model = this.datamodel;
|
|
198
|
+
const fields = model.fields;
|
|
199
|
+
let row_id_seed = model.getRowPrimarykeyValues(row).join(' ');
|
|
200
|
+
let primary_key_values = [];
|
|
201
|
+
let urlencode_row = "";
|
|
202
|
+
for (let i = 0; i < fields.length; i++) {
|
|
203
|
+
const f = fields[i];
|
|
204
|
+
if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
let value = f.serializeCell(row[i]);
|
|
208
|
+
if ((value === undefined)) { // || (value === null)) {
|
|
209
|
+
// console.log("OINOModelSet._writeRowUrlencode undefined field value:" + fields[i].name)
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.urlencode, primary_key_values, f.name + " " + row_id_seed);
|
|
213
|
+
if (urlencode_row != "") {
|
|
214
|
+
urlencode_row += "&";
|
|
215
|
+
}
|
|
216
|
+
urlencode_row += OINOStr.encode(f.name, OINOContentType.urlencode) + "=" + value;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
urlencode_row = OINOStr.encode(OINOConfig.OINO_ID_FIELD, OINOContentType.urlencode) + "=" + OINOStr.encode(OINOConfig.printOINOId(primary_key_values), OINOContentType.urlencode) + "&" + urlencode_row;
|
|
220
|
+
return urlencode_row;
|
|
221
|
+
}
|
|
222
|
+
async _writeStringUrlencode() {
|
|
223
|
+
let result = "";
|
|
224
|
+
let line_count = 0;
|
|
225
|
+
while (!this.dataset.isEof()) {
|
|
226
|
+
const row = this.dataset.getRow();
|
|
227
|
+
result += this._writeRowUrlencode(row) + "\r\n";
|
|
228
|
+
await this.dataset.next();
|
|
229
|
+
line_count += 1;
|
|
230
|
+
}
|
|
231
|
+
if (line_count > 1) {
|
|
232
|
+
OINOLog.warning("@oino-ts/db", "OINOModelSet", "_writeStringUrlencode", "Content type " + OINOContentType.urlencode + " does not officially support multiline content!", {});
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
_exportRow(row) {
|
|
237
|
+
// console.log("OINOModelSet._exportRow: row=" + row)
|
|
238
|
+
const model = this.datamodel;
|
|
239
|
+
const fields = model.fields;
|
|
240
|
+
let row_id_seed = model.getRowPrimarykeyValues(row).join(' ');
|
|
241
|
+
let primary_key_values = [];
|
|
242
|
+
let result = {};
|
|
243
|
+
for (let i = 0; i < fields.length; i++) {
|
|
244
|
+
const f = fields[i];
|
|
245
|
+
if (f.fieldParams.isPrimaryKey) {
|
|
246
|
+
primary_key_values.push(f.serializeCell(row[i]) || "");
|
|
247
|
+
}
|
|
248
|
+
if ((this.queryParams?.select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
let value = f.datasource.parseValueAsCell(row[i], f.nativeType); // retain original value without serialization
|
|
252
|
+
if (value === undefined) {
|
|
253
|
+
// skip undefined values
|
|
254
|
+
}
|
|
255
|
+
else if (value === null) { // differentiate null and undefined
|
|
256
|
+
result[f.name] = null;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
result[f.name] = value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
result[OINOConfig.OINO_ID_FIELD] = OINOConfig.printOINOId(primary_key_values);
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Serialize model set in the given format.
|
|
267
|
+
*
|
|
268
|
+
* @param [contentType=OINOContentType.json] serialization content type
|
|
269
|
+
*
|
|
270
|
+
*/
|
|
271
|
+
async writeString(contentType = OINOContentType.json) {
|
|
272
|
+
let result = "";
|
|
273
|
+
if (contentType == OINOContentType.csv) {
|
|
274
|
+
result += await this._writeStringCsv();
|
|
275
|
+
}
|
|
276
|
+
else if (contentType == OINOContentType.json) {
|
|
277
|
+
result += await this._writeStringJson();
|
|
278
|
+
}
|
|
279
|
+
else if (contentType == OINOContentType.formdata) {
|
|
280
|
+
result += await this._writeStringFormdata();
|
|
281
|
+
}
|
|
282
|
+
else if (contentType == OINOContentType.urlencode) {
|
|
283
|
+
result += await this._writeStringUrlencode();
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
OINOLog.error("@oino-ts/db", "OINOModelSet", "writeString", "Content type is only for input!", { contentType: contentType });
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get value of given field in the current row. Undefined if no rows,
|
|
292
|
+
* field not found or value does not exist.
|
|
293
|
+
*
|
|
294
|
+
* @param fieldName name of the field
|
|
295
|
+
* @param serialize serialize the value
|
|
296
|
+
*
|
|
297
|
+
*/
|
|
298
|
+
getValueByFieldName(fieldName, serialize = false) {
|
|
299
|
+
let result = undefined;
|
|
300
|
+
if (!this.dataset.isEof()) {
|
|
301
|
+
const current_row = this.dataset.getRow();
|
|
302
|
+
const field_index = this.datamodel.findFieldIndexByName(fieldName);
|
|
303
|
+
if (field_index >= 0) {
|
|
304
|
+
result = current_row[field_index];
|
|
305
|
+
if (serialize) {
|
|
306
|
+
result = this.datamodel.fields[field_index].serializeCell(result);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Export all rows as a record with OINOId as key and object with row cells as values.
|
|
314
|
+
*
|
|
315
|
+
* @param idFieldName optional field name to use as key instead of OINOId
|
|
316
|
+
*/
|
|
317
|
+
async exportAsRecord(idFieldName) {
|
|
318
|
+
const result = {};
|
|
319
|
+
const row_id_field = idFieldName || OINOConfig.OINO_ID_FIELD;
|
|
320
|
+
while (!this.dataset.isEof()) {
|
|
321
|
+
const row_data = this.dataset.getRow();
|
|
322
|
+
const row_export = this._exportRow(row_data);
|
|
323
|
+
const row_id = row_export[row_id_field];
|
|
324
|
+
result[row_id] = row_export;
|
|
325
|
+
await this.dataset.next();
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
}
|