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