@oino-ts/db 0.21.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/OINODb.js +6 -144
  2. package/dist/cjs/OINODbApi.js +50 -318
  3. package/dist/cjs/OINODbConfig.js +10 -10
  4. package/dist/cjs/OINODbConstants.js +10 -0
  5. package/dist/cjs/OINODbDataField.js +28 -70
  6. package/dist/cjs/OINODbDataModel.js +29 -143
  7. package/dist/cjs/OINODbFactory.js +2 -2
  8. package/dist/cjs/OINODbModelSet.js +23 -23
  9. package/dist/cjs/OINODbQueryParams.js +201 -0
  10. package/dist/cjs/index.js +12 -41
  11. package/dist/esm/OINODb.js +6 -142
  12. package/dist/esm/OINODbApi.js +49 -314
  13. package/dist/esm/OINODbConstants.js +7 -0
  14. package/dist/esm/OINODbDataModel.js +29 -143
  15. package/dist/esm/OINODbFactory.js +1 -1
  16. package/dist/esm/OINODbQueryParams.js +194 -0
  17. package/dist/esm/index.js +4 -14
  18. package/dist/types/OINODb.d.ts +6 -173
  19. package/dist/types/OINODbApi.d.ts +18 -104
  20. package/dist/types/OINODbConstants.d.ts +23 -0
  21. package/dist/types/OINODbDataModel.d.ts +7 -61
  22. package/dist/types/OINODbFactory.d.ts +5 -2
  23. package/dist/types/OINODbQueryParams.d.ts +72 -0
  24. package/dist/types/index.d.ts +4 -108
  25. package/package.json +37 -37
  26. package/src/OINODb.ts +99 -348
  27. package/src/OINODbApi.test.ts +507 -498
  28. package/src/OINODbApi.ts +389 -667
  29. package/src/OINODbConstants.ts +32 -0
  30. package/src/OINODbDataModel.ts +191 -307
  31. package/src/OINODbFactory.ts +73 -68
  32. package/src/OINODbQueryParams.ts +203 -0
  33. package/src/index.ts +6 -118
  34. package/src/OINODbConfig.ts +0 -98
  35. package/src/OINODbDataField.ts +0 -405
  36. package/src/OINODbModelSet.ts +0 -353
  37. package/src/OINODbParser.ts +0 -438
  38. package/src/OINODbSqlParams.ts +0 -593
  39. package/src/OINODbSwagger.ts +0 -209
package/src/OINODbApi.ts CHANGED
@@ -1,668 +1,390 @@
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 { Buffer } from "node:buffer";
8
- import { OINOLog, OINOResult, OINOHttpRequest, type OINOHttpRequestInit, OINOBenchmark, OINOHttpResult, OINOHtmlTemplate, OINOContentType, OINO_ERROR_PREFIX } from "@oino-ts/common";
9
- import { OINODbApiParams, OINODb, OINODbDataSet, OINODbDataModel, OINODbDataField, OINOStringDataField, OINODataRow, OINODataCell, OINODbModelSet, OINODbConfig, OINONumberDataField, OINODbParser, OINODatetimeDataField, OINODbSqlParams, OINODbSqlAggregate, OINODbSqlSelect, OINODbSqlFilter, OINODbSqlOrder, OINODbSqlLimit, OINODbSqlBooleanOperation } from "./index.js"
10
- import { OINOHashid } from "@oino-ts/hashid"
11
-
12
- export type OINODbApiData = string|OINODataRow[]|Buffer|Uint8Array|object|null
13
-
14
- export interface OINODbApiRequestInit extends OINOHttpRequestInit {
15
- rowId?: string
16
- rowData?: OINODbApiData
17
- sqlParams?: OINODbSqlParams
18
- filter?: OINODbSqlFilter|string
19
- order?: OINODbSqlOrder|string
20
- limit?: OINODbSqlLimit|string
21
- aggregate?: OINODbSqlAggregate|string
22
- select?: OINODbSqlSelect|string
23
- }
24
-
25
- export class OINODbApiRequest extends OINOHttpRequest {
26
- rowId:string
27
- rowData:OINODbApiData
28
- sqlParams:OINODbSqlParams
29
-
30
- constructor (init: OINODbApiRequestInit) {
31
- super(init)
32
- this.rowId = init?.rowId || ""
33
- this.rowData = init?.rowData || null // rowData is not compatible with OINOHttpRequest body so it's not automatically set, caller can set both if needed
34
- this.sqlParams = init?.sqlParams || {}
35
-
36
- if (init?.filter) {
37
- if (init.filter instanceof OINODbSqlFilter) {
38
- this.sqlParams.filter = init.filter
39
- } else {
40
- this.sqlParams.filter = OINODbSqlFilter.parse(init.filter)
41
- }
42
- }
43
- if (!this.sqlParams.filter) {
44
- const filter_params = this.url?.searchParams.getAll(OINODbConfig.OINODB_SQL_FILTER_PARAM) || []
45
- for (let i=0; i<filter_params.length; i++) {
46
- const f = OINODbSqlFilter.parse(filter_params[i])
47
- if (i > 0) {
48
- this.sqlParams.filter = OINODbSqlFilter.combine(this.sqlParams.filter, OINODbSqlBooleanOperation.and, f)
49
- } else {
50
- this.sqlParams.filter = f
51
- }
52
- }
53
- }
54
- if (init?.order) {
55
- if (init.order instanceof OINODbSqlOrder) {
56
- this.sqlParams.order = init.order
57
- } else {
58
- this.sqlParams.order = OINODbSqlOrder.parse(init.order)
59
- }
60
- }
61
- if (!this.sqlParams.order) {
62
- const order_param = this.url?.searchParams.get(OINODbConfig.OINODB_SQL_ORDER_PARAM)
63
- if (order_param) {
64
- this.sqlParams.order = OINODbSqlOrder.parse(order_param)
65
- }
66
- }
67
- if (init?.limit) {
68
- if (init.limit instanceof OINODbSqlLimit) {
69
- this.sqlParams.limit = init.limit
70
- } else {
71
- this.sqlParams.limit = OINODbSqlLimit.parse(init.limit)
72
- }
73
- }
74
- if (!this.sqlParams.limit) {
75
- const limit_param = this.url?.searchParams.get(OINODbConfig.OINODB_SQL_LIMIT_PARAM)
76
- if (limit_param) {
77
- this.sqlParams.limit = OINODbSqlLimit.parse(limit_param)
78
- }
79
- }
80
- if (init?.aggregate) {
81
- if (init.aggregate instanceof OINODbSqlAggregate) {
82
- this.sqlParams.aggregate = init.aggregate
83
- } else {
84
- this.sqlParams.aggregate = OINODbSqlAggregate.parse(init.aggregate)
85
- }
86
- }
87
- if (!this.sqlParams.aggregate) {
88
- const aggregate_param = this.url?.searchParams.get(OINODbConfig.OINODB_SQL_AGGREGATE_PARAM)
89
- if (aggregate_param) {
90
- this.sqlParams.aggregate = OINODbSqlAggregate.parse(aggregate_param)
91
- }
92
- }
93
- if (init?.select) {
94
- if (init.select instanceof OINODbSqlSelect) {
95
- this.sqlParams.select = init.select
96
- } else {
97
- this.sqlParams.select = OINODbSqlSelect.parse(init.select)
98
- }
99
- }
100
- if (!this.sqlParams.select) {
101
- const select_param = this.url?.searchParams.get(OINODbConfig.OINODB_SQL_SELECT_PARAM)
102
- if (select_param) {
103
- this.sqlParams.select = OINODbSqlSelect.parse(select_param)
104
- }
105
- }
106
- }
107
- static async fromFetchRequest(request: Request, rowId?: string, rowData?: OINODbApiData, sqlParams?: OINODbSqlParams): Promise<OINODbApiRequest> {
108
- return new OINODbApiRequest({
109
- url: new URL(request.url),
110
- method: request.method,
111
- headers: Object.fromEntries(request.headers as any),
112
- rowId: rowId,
113
- rowData: rowData ?? Buffer.from(await request.arrayBuffer()),
114
- sqlParams: sqlParams
115
- })
116
- }
117
-
118
- static fromHttpRequest(request: OINOHttpRequest, rowId?: string, rowData?: OINODbApiData, sqlParams?: OINODbSqlParams): OINODbApiRequest {
119
- return new OINODbApiRequest({
120
- url: typeof request.url === "string" ? new URL(request.url) : request.url,
121
- method: request.method,
122
- headers: Object.fromEntries(request.headers as any),
123
- rowId: rowId,
124
- rowData: rowData ?? request.bodyAsBuffer(),
125
- requestType: request.requestType,
126
- responseType: request.responseType,
127
- multipartBoundary: request.multipartBoundary,
128
- lastModified: request.lastModified,
129
- sqlParams: sqlParams
130
- })
131
- }
132
- }
133
-
134
- /**
135
- * OINO API request result object with returned data and/or http status code/message and
136
- * error / warning messages.
137
- *
138
- */
139
- export class OINODbApiResult extends OINOResult {
140
- /** DbApi request params */
141
- request: OINODbApiRequest
142
-
143
- /** Returned data if any */
144
- data?: OINODbModelSet;
145
-
146
- /**
147
- * Constructor of OINODbApiResult.
148
- *
149
- * @param request DbApi request parameters
150
- * @param data result data
151
- *
152
- */
153
- constructor (request:OINODbApiRequest, data?:OINODbModelSet) {
154
- super()
155
- this.request = request
156
- this.data = data
157
- }
158
-
159
- /**
160
- * Creates a HTTP Response from API results.
161
- *
162
- * @param headers Headers to include in the response
163
- *
164
- */
165
- async writeApiResponse(headers:Record<string, string> = {}):Promise<Response> {
166
- let response:Response|null = null
167
- if (this.success && this.data) {
168
- const body = await this.data.writeString(this.request.responseType)
169
- response = new Response(body, {status:this.status, statusText: this.statusText, headers: headers })
170
- } else {
171
- response = new Response(JSON.stringify(this, null, 3), {status:this.status, statusText: this.statusText, headers: headers })
172
- }
173
- for (let i=0; i<this.messages.length; i++) {
174
- response.headers.set('X-OINO-MESSAGE-' + i, this.messages[i])
175
- }
176
- return Promise.resolve(response)
177
- }
178
- }
179
-
180
- /**
181
- * Specialized HTML template that can render ´OINODbApiResult´.
182
- *
183
- */
184
- export class OINODbHtmlTemplate extends OINOHtmlTemplate {
185
- /** Locale validation regex */
186
- static LOCALE_REGEX:RegExp = /^(\w\w)(\-\w\w)?$/
187
- /** Locale formatter */
188
- protected _locale:Intl.DateTimeFormat|null
189
- protected _numberDecimals:number = -1
190
-
191
- /**
192
- * Constructor of OINODbHtmlTemplate.
193
- *
194
- * @param template HTML template string
195
- * @param numberDecimals Number of decimals to use for numbers, -1 for no formatting
196
- * @param dateLocaleStr Datetime format string, either "iso" for ISO8601 or "default" for system default or valid locale string
197
- * @param dateLocaleStyle Datetime format style, either "short/medium/long/full" or Intl.DateTimeFormat options
198
- *
199
- */
200
- constructor (template:string, numberDecimals:number=-1, dateLocaleStr:string="", dateLocaleStyle:string|any="") {
201
- super(template)
202
- let locale_opts:any
203
- if ((dateLocaleStyle == null) || (dateLocaleStyle == "")) {
204
- locale_opts = { dateStyle: "medium", timeStyle: "medium" }
205
- } else if (typeof dateLocaleStyle == "string") {
206
- locale_opts = { dateStyle: dateLocaleStyle, timeStyle: dateLocaleStyle }
207
- } else {
208
- locale_opts = dateLocaleStyle
209
- }
210
- this._locale = null
211
- this._numberDecimals = numberDecimals
212
-
213
- if ((dateLocaleStr != null) && (dateLocaleStr != "") && OINODbHtmlTemplate.LOCALE_REGEX.test(dateLocaleStr)) {
214
- try {
215
- this._locale = new Intl.DateTimeFormat(dateLocaleStr, locale_opts)
216
- } catch (e:any) {}
217
- }
218
- }
219
-
220
- /**
221
- * Creates HTML Response from API modelset.
222
- *
223
- * @param modelset OINO API dataset
224
- * @param overrideValues values to override in the data
225
- *
226
- */
227
- async renderFromDbData(modelset:OINODbModelSet, overrideValues?:any):Promise<OINOHttpResult> {
228
- OINOBenchmark.startMetric("OINOHtmlTemplate", "renderFromDbData")
229
- let html:string = ""
230
- const dataset:OINODbDataSet|undefined = modelset.dataset
231
- const datamodel:OINODbDataModel = modelset.datamodel
232
- const api:OINODbApi = modelset.datamodel.api
233
- const modified_index = datamodel.findFieldIndexByName(api.params.cacheModifiedField || "")
234
- let last_modified:number = this.modified
235
-
236
- while (!dataset.isEof()) {
237
- const row:OINODataRow = dataset.getRow()
238
- if (modified_index >= 0) {
239
- last_modified = Math.max(last_modified, new Date(row[modified_index] as Date).getTime())
240
- }
241
- let row_id_seed:string = datamodel.getRowPrimarykeyValues(row).join(' ')
242
- let primary_key_values:string[] = []
243
- this.clearVariables()
244
- this.setVariableFromValue(OINODbConfig.OINODB_ID_FIELD, "")
245
- for (let i=0; i<datamodel.fields.length; i++) {
246
- const f:OINODbDataField = datamodel.fields[i]
247
- let value:string|null|undefined
248
- if ((f instanceof OINODatetimeDataField) && (this._locale != null)) {
249
- value = f.serializeCellWithLocale(row[i], this._locale)
250
-
251
- } else if ((f instanceof OINONumberDataField) && (this._numberDecimals >= 0) && (typeof row[i] === "number")) {
252
- // console.debug("renderFromDbData number decimals", { field: f.name, value: row[i], type: typeof row[i] });
253
- value = (row[i]! as number).toFixed(this._numberDecimals)
254
-
255
- } else {
256
- value = f.serializeCell(row[i])
257
- }
258
- if (f.fieldParams.isPrimaryKey || f.fieldParams.isForeignKey) {
259
- if (value && (f instanceof OINONumberDataField) && (datamodel.api.hashid)) {
260
- value = datamodel.api.hashid.encode(value, f.name + " " + row_id_seed)
261
- }
262
- if (f.fieldParams.isPrimaryKey) {
263
- primary_key_values.push(value || "")
264
- }
265
- }
266
- this.setVariableFromValue(f.name, value || "")
267
- }
268
- this.setVariableFromProperties(overrideValues)
269
- this.setVariableFromValue(OINODbConfig.OINODB_ID_FIELD, OINODbConfig.printOINOId(primary_key_values))
270
- html += this._renderHtml() + "\r\n"
271
- await dataset.next()
272
- }
273
- this.modified = last_modified
274
- const result:OINOHttpResult = this._createHttpResult(html)
275
- OINOBenchmark.endMetric("OINOHtmlTemplate", "renderFromDbData")
276
- return result
277
- }
278
-
279
- }
280
-
281
-
282
- /**
283
- * API class with method to process HTTP REST requests.
284
- *
285
- */
286
- export class OINODbApi {
287
- /** Enable debug output on errors */
288
- private _debugOnError:boolean = false
289
-
290
- /** API database reference */
291
- readonly db: OINODb
292
-
293
- /** API datamodel */
294
- readonly datamodel: OINODbDataModel
295
-
296
- /** API parameters */
297
- readonly params: OINODbApiParams
298
-
299
- /** API hashid */
300
- readonly hashid:OINOHashid|null
301
-
302
- /**
303
- * Constructor of API object.
304
- * NOTE! OINODb.initDatamodel must be called if created manually instead of the factory.
305
- *
306
- * @param db database for the API
307
- * @param params parameters for the API
308
- *
309
- */
310
- constructor (db: OINODb, params:OINODbApiParams) {
311
- if (!params.tableName) {
312
- throw new Error(OINO_ERROR_PREFIX + ": OINODbApiParams needs to define a table name!")
313
- }
314
- this.db = db
315
- this.params = params
316
- this.datamodel = new OINODbDataModel(this)
317
- if (this.params.hashidKey) {
318
- this.hashid = new OINOHashid(this.params.hashidKey, this.db.name, this.params.hashidLength, this.params.hashidStaticIds)
319
- } else {
320
- this.hashid = null
321
- }
322
- }
323
-
324
- private _validateRow(result:OINODbApiResult, row:OINODataRow, requirePrimaryKey:boolean):void {
325
- let field:OINODbDataField
326
- for (let i=0; i<this.datamodel.fields.length; i++) {
327
- field = this.datamodel.fields[i]
328
- const val:OINODataCell = row[i]
329
- if ((val === null) && ((field.fieldParams.isNotNull)||(field.fieldParams.isPrimaryKey))) { // null is a valid SQL value except if it's not allowed
330
- result.setError(405, "Field '" + field.name + "' is not allowed to be NULL!", "ValidateRowValues")
331
-
332
- } else if ((val === undefined) && (requirePrimaryKey) && (field.fieldParams.isPrimaryKey) && (!field.fieldParams.isAutoInc)) {
333
- result.setError(405, "Primary key '" + field.name + "' is not autoinc and missing from the data!", "ValidateRowValues")
334
-
335
- } else if ((val !== undefined) && (this.params.failOnUpdateOnAutoinc) && (field.fieldParams.isAutoInc)) {
336
- result.setError(405, "Autoinc field '" + field.name + "' can't be updated!", "ValidateRowValues")
337
-
338
- } else {
339
- if ((field instanceof OINOStringDataField) && ((field.maxLength > 0))){
340
- const str_val = val?.toString() || ""
341
- if (str_val.length > field.maxLength) {
342
- if (this.params.failOnOversizedValues) {
343
- result.setError(405, "Field '" + field.name + "' length (" + str_val.length + ") exceeds maximum (" + field.maxLength + ") and can't be set!", "ValidateRowValues")
344
- } else {
345
- result.addWarning("Field '" + field.name + "' length (" + str_val.length + ") exceeds maximum (" + field.maxLength + ") and might truncate or fail.", "ValidateRowValues")
346
- }
347
- }
348
- }
349
-
350
- }
351
- }
352
- //logDebug("OINODbApi.validateHttpValues", {result:result})
353
- }
354
-
355
- private _parseData(httpResult:OINODbApiResult, request:OINODbApiRequest):OINODataRow[] {
356
- let rows:OINODataRow[] = []
357
- const data = request.rowData ?? request.body
358
- try {
359
- if (Array.isArray(data)) {
360
- rows = data as OINODataRow[]
361
- } else if (data != null) {
362
- rows = OINODbParser.createRows(this.datamodel, data, request.requestType, request.multipartBoundary)
363
- }
364
-
365
- } catch (e:any) {
366
- httpResult.setError(400, "Invalid data: " + e.message, "DoRequest")
367
- }
368
- return rows
369
- }
370
-
371
- private async _doGet(result:OINODbApiResult, rowId:string, request:OINODbApiRequest):Promise<void> {
372
- let sql:string = ""
373
- try {
374
- sql = this.datamodel.printSqlSelect(rowId, request.sqlParams || {})
375
- OINOLog.debug("@oino-ts/db", "OINODbApi", "_doGet", "Print SQL", {sql:sql})
376
- const sql_res:OINODbDataSet = await this.db.sqlSelect(sql)
377
- if (sql_res.success == false) {
378
- result.setError(500, sql_res.statusText, "DoGet")
379
- if (this._debugOnError) {
380
- result.addDebug("OINO GET SQL [" + sql + "]", "DoPut")
381
- }
382
- } else {
383
- result.data = new OINODbModelSet(this.datamodel, sql_res, request.sqlParams)
384
- }
385
- } catch (e:any) {
386
- result.setError(500, "Unhandled exception in doGet: " + e.message, "DoGet")
387
- OINOLog.exception("@oino-ts/db", "OINODbApi", "_doGet", "exception in get request", {message:e.message, stack:e.stack})
388
- if (this._debugOnError) {
389
- result.addDebug("OINO GET SQL [" + sql + "]", "DoGet")
390
- }
391
- }
392
- }
393
-
394
- private async _doPost(result:OINODbApiResult, rows:OINODataRow[], request:OINODbApiRequest):Promise<void> {
395
- let sql:string = ""
396
- try {
397
- for (let i=0; i<rows.length; i++) {
398
- this._validateRow(result, rows[i], this.params.failOnInsertWithoutKey||false)
399
- if (result.success) {
400
- sql += this.datamodel.printSqlInsert(rows[i])
401
-
402
- } else if (this.params.failOnAnyInvalidRows == false) {
403
- result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
404
- }
405
- }
406
- if ((sql == "") && result.success) {
407
- result.setError(405, "No valid rows for POST!", "DoPost")
408
-
409
- } else if (result.success) {
410
- OINOLog.debug("@oino-ts/db", "OINODbApi", "_doPost", "Print SQL", {sql:sql})
411
- const sql_res:OINODbDataSet = await this.db.sqlExec(sql)
412
- if (sql_res.success == false) {
413
- result.setError(500, sql_res.statusText, "DoPost")
414
- if (this._debugOnError) {
415
- result.addDebug("OINO POST MESSAGES [" + sql_res.statusText + "]", "DoPost")
416
- result.addDebug("OINO POST SQL [" + sql + "]", "DoPost")
417
- }
418
- } else if (this.params.returnInsertedIds) {
419
- result.data = new OINODbModelSet(this.datamodel, sql_res, request.sqlParams) // return the inserted ids as data
420
- }
421
- }
422
- } catch (e:any) {
423
- result.setError(500, "Unhandled exception in doPost: " + e.message, "DoPost")
424
- OINOLog.exception("@oino-ts/db", "OINODbApi", "_doPost", "exception in post request", {message:e.message, stack:e.stack})
425
- if (this._debugOnError) {
426
- result.addDebug("OINO POST SQL [" + sql + "]", "DoPost")
427
- }
428
- }
429
- }
430
-
431
- private async _doPut(result:OINODbApiResult, id:string|null, rows:OINODataRow[]):Promise<void> {
432
- let sql:string = ""
433
- try {
434
- // this._validateRowValues(result, row, false)
435
- for (let i=0; i<rows.length; i++) {
436
- const row_id = id || OINODbConfig.printOINOId(this.datamodel.getRowPrimarykeyValues(rows[i], this.hashid != null))
437
- this._validateRow(result, rows[i], this.params.failOnInsertWithoutKey||false)
438
- if (result.success) {
439
- sql += this.datamodel.printSqlUpdate(row_id, rows[i])
440
-
441
- } else if (this.params.failOnAnyInvalidRows == false) {
442
- result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
443
- }
444
- }
445
- if ((sql == "") && result.success) {
446
- result.setError(405, "No valid rows for PUT!", "DoPut") // only set error if there are multiple rows and no valid sql was created
447
-
448
- } else if (result.success) {
449
- OINOLog.debug("@oino-ts/db", "OINODbApi", "_doPut", "Print SQL", {sql:sql})
450
- const sql_res:OINODbDataSet = await this.db.sqlExec(sql)
451
- if (sql_res.success == false) {
452
- result.setError(500, sql_res.statusText, "DoPut")
453
- if (this._debugOnError) {
454
- result.addDebug("OINO PUT MESSAGES [" + sql_res.statusText + "]", "DoPut")
455
- result.addDebug("OINO PUT SQL [" + sql + "]", "DoPut")
456
- }
457
- }
458
- }
459
- } catch (e:any) {
460
- result.setError(500, "Unhandled exception: " + e.message, "DoPut")
461
- OINOLog.exception("@oino-ts/db", "OINODbApi", "_doPut", "exception in put request", {message:e.message, stack:e.stack})
462
- if (this._debugOnError) {
463
- result.addDebug("OINO PUT SQL [" + sql + "]", "DoPut")
464
- }
465
- }
466
- }
467
-
468
- private async _doDelete(result:OINODbApiResult, id:string|null, rows:OINODataRow[]|null):Promise<void> {
469
- let sql:string = ""
470
- try {
471
- if (rows != null) {
472
- for (let i=0; i<rows.length; i++) {
473
- const row_id = OINODbConfig.printOINOId(this.datamodel.getRowPrimarykeyValues(rows[i], this.hashid != null))
474
- if (row_id) {
475
- sql += this.datamodel.printSqlDelete(row_id)
476
- } else if (this.params.failOnAnyInvalidRows == false) {
477
- result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
478
- }
479
- }
480
- } else if (id) {
481
- sql = this.datamodel.printSqlDelete(id)
482
- }
483
- if ((sql == "") && result.success) {
484
- result.setError(405, "No valid rows for DELETE!", "DoDelete") // only set error if there are multiple rows and no valid sql was created
485
-
486
- } else if (result.success) {
487
-
488
- OINOLog.debug("@oino-ts/db", "OINODbApi", "_doDelete", "Print SQL", {sql:sql})
489
- const sql_res:OINODbDataSet = await this.db.sqlExec(sql)
490
- if (sql_res.success == false) {
491
- result.setError(500, sql_res.statusText, "DoDelete")
492
- if (this._debugOnError) {
493
- result.addDebug("OINO DELETE MESSAGES [" + sql_res.statusText + "]", "DoDelete")
494
- result.addDebug("OINO DELETE SQL [" + sql + "]", "DoDelete")
495
- }
496
- }
497
- }
498
- } catch (e:any) {
499
- result.setError(500, "Unhandled exception: " + e.message, "DoDelete")
500
- OINOLog.exception("@oino-ts/db", "OINODbApi", "_doDelete", "exception in delete request", {message:e.message, stack:e.stack})
501
- if (this._debugOnError) {
502
- result.addDebug("OINO DELETE SQL [" + sql + "]", "DoDelete")
503
- }
504
- }
505
- }
506
-
507
- /**
508
- * Enable or disable debug output on errors.
509
- *
510
- * @param debugOnError true to enable debug output on errors, false to disable
511
- */
512
- setDebugOnError(debugOnError:boolean) {
513
- this._debugOnError = debugOnError
514
- }
515
-
516
- /**
517
- * Method for handling a HTTP REST request with GET, POST, PUT, DELETE corresponding to
518
- * SQL select, insert, update and delete.
519
- *
520
- * @param request OINO HTTP request object containing all parameters of the REST request
521
- * @param rowId URL id of the REST request
522
- * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
523
- * @param sqlParams SQL parameters for the REST request
524
- *
525
- */
526
- async doHttpRequest(request: OINOHttpRequest, rowId:string, rowData:OINODbApiData, sqlParams:OINODbSqlParams):Promise<OINODbApiResult> {
527
- const api_request = OINODbApiRequest.fromHttpRequest(request, rowId, rowData, sqlParams)
528
- return this.doApiRequest(api_request)
529
- }
530
- /**
531
- * Method for handling a HTTP REST request with GET, POST, PUT, DELETE corresponding to
532
- * SQL select, insert, update and delete.
533
- *
534
- * @param method HTTP method of the REST request
535
- * @param rowId URL id of the REST request
536
- * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
537
- * @param sqlParams SQL parameters for the REST request
538
- * @param contentType content type of the HTTP body data, default is JSON
539
- *
540
- */
541
- async doRequest(method:string, rowId:string, rowData:OINODbApiData, sqlParams:OINODbSqlParams, contentType:OINOContentType = OINOContentType.json):Promise<OINODbApiResult> {
542
- return this.doApiRequest(new OINODbApiRequest({method: method, rowId: rowId, rowData: rowData, sqlParams: sqlParams, requestType: contentType}))
543
- }
544
-
545
- async doApiRequest(request:OINODbApiRequest):Promise<OINODbApiResult> {
546
- OINOBenchmark.startMetric("OINODbApi", "doRequest." + request.method)
547
- OINOLog.debug("@oino-ts/db", "OINODbApi", "doRequest", "Request", {method:request.method, id:request.rowId, data:request.rowData})
548
- let result:OINODbApiResult = new OINODbApiResult(request)
549
- let rows:OINODataRow[] = []
550
- if ((request.method == "POST") || (request.method == "PUT")) {
551
- rows = this._parseData(result, request)
552
- }
553
- if (request.method == "GET") {
554
- await this._doGet(result, request.rowId, request)
555
-
556
- } else if (request.method == "PUT") {
557
- if (!request.rowId) {
558
- result.setError(400, "HTTP PUT method requires an URL ID for the row that is updated!", "DoRequest")
559
-
560
- } else if (rows.length != 1) {
561
- result.setError(400, "HTTP PUT method requires exactly one row in the body data!", "DoRequest")
562
-
563
- } else {
564
- try {
565
- await this._doPut(result, request.rowId, rows)
566
-
567
- } catch (e:any) {
568
- result.setError(500, "Unhandled exception in HTTP PUT doRequest: " + e.message, "DoRequest")
569
- }
570
- }
571
- } else if (request.method == "POST") {
572
- if (request.rowId) {
573
- result.setError(400, "HTTP POST method must not have an URL ID as it does not target an existing row but creates a new one!", "DoRequest")
574
-
575
- } else if (rows.length == 0) {
576
- result.setError(400, "HTTP POST method requires at least one row in the body data!", "DoRequest")
577
-
578
- } else {
579
- try {
580
- await this._doPost(result, rows, request)
581
-
582
- } catch (e:any) {
583
- result.setError(500, "Unhandled exception in HTTP POST doRequest: " + e.message, "DoRequest")
584
- }
585
- }
586
- } else if (request.method == "DELETE") {
587
- if (!request.rowId) {
588
- result.setError(400, "HTTP DELETE method requires an id!", "DoRequest")
589
-
590
- } else {
591
- try {
592
- await this._doDelete(result, request.rowId, null)
593
-
594
- } catch (e:any) {
595
- result.setError(500, "Unhandled exception in HTTP DELETE doRequest: " + e.message, "DoRequest")
596
- }
597
- }
598
- } else {
599
- result.setError(405, "Unsupported HTTP method '" + request.method + "' for REST request", "DoRequest")
600
- }
601
- OINOBenchmark.endMetric("OINODbApi", "doRequest." + request.method, result.status != 500)
602
- return Promise.resolve(result)
603
- }
604
-
605
- /**
606
- * Method for handling a HTTP REST request with batch update using PUT or DELETE methods.
607
- *
608
- * @param method HTTP method of the REST request
609
- * @param rowId URL id of the REST request
610
- * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
611
- *
612
- */
613
- async doBatchUpdate(method:string, rowId:string, rowData:OINODbApiData, sqlParams?: OINODbSqlParams):Promise<OINODbApiResult> {
614
- return this.doApiRequest(new OINODbApiRequest({ method: method, rowId: rowId, rowData: rowData, sqlParams: sqlParams }))
615
- }
616
- /**
617
- * Method for handling a HTTP REST request with batch update using PUT or DELETE methods.
618
- *
619
- * @param request HTTP URL parameters as key-value-pairs
620
- *
621
- */
622
- async runBatchUpdate(request:OINODbApiRequest):Promise<OINODbApiResult> {
623
- OINOLog.debug("@oino-ts/db", "OINODbApi", "doBatchUpdate", "Request", {request:request, data:request.rowData})
624
- let result:OINODbApiResult = new OINODbApiResult(request)
625
- if ((request.method != "PUT") && (request.method != "DELETE")) {
626
- result.setError(500, "Batch update only supports PUT and DELETE methods!", "DoBatchUpdate")
627
- return Promise.resolve(result)
628
- }
629
- OINOBenchmark.startMetric("OINODbApi", "doBatchUpdate." + request.method)
630
- const rows:OINODataRow[] = [] = this._parseData(result, request)
631
- if (request.method == "PUT") {
632
-
633
- try {
634
- await this._doPut(result, null, rows)
635
-
636
- } catch (e:any) {
637
- result.setError(500, "Unhandled exception in HTTP PUT doRequest: " + e.message, "DoBatchUpdate")
638
- }
639
-
640
- } else if (request.method == "DELETE") {
641
- try {
642
- await this._doDelete(result, null, rows)
643
-
644
- } catch (e:any) {
645
- result.setError(500, "Unhandled exception in HTTP DELETE doRequest: " + e.message, "DoBatchUpdate")
646
- }
647
- }
648
- OINOBenchmark.endMetric("OINODbApi", "doBatchUpdate." + request.method, result.status != 500)
649
- return Promise.resolve(result)
650
- }
651
-
652
- /**
653
- * Method to check if a field is included in the API params.
654
- *
655
- * @param fieldName name of the field
656
- *
657
- */
658
-
659
- public isFieldIncluded(fieldName:string):boolean {
660
- const params = this.params
661
- return (
662
- ((params.excludeFieldPrefix == undefined) || (params.excludeFieldPrefix == "") || (fieldName.startsWith(params.excludeFieldPrefix) == false)) &&
663
- ((params.excludeFields == undefined) || (params.excludeFields.length == 0) || (params.excludeFields.indexOf(fieldName) < 0)) &&
664
- ((params.includeFields == undefined) || (params.includeFields.length == 0) || (params.includeFields.indexOf(fieldName) >= 0))
665
- )
666
- }
667
-
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 { OINOApi, OINOApiParams, OINOLog, OINOBenchmark, OINOHttpRequest, OINOHttpResult, OINOApiRequest, OINOApiResult, OINOHtmlTemplate, OINOContentType, OINODataRow, OINODataCell, OINODataSet, OINODataField, OINOStringDataField, OINOConfig, OINONumberDataField, OINODatetimeDataField, OINOQueryParams, OINOApiData, OINOModelSet, OINOParser, OINO_ERROR_PREFIX } from "@oino-ts/common";
8
+ import { OINODb } from "./OINODb.js"
9
+ import { OINODbDataModel } from "./OINODbDataModel.js"
10
+
11
+
12
+ /**
13
+ * API class with method to process HTTP REST requests.
14
+ *
15
+ */
16
+ export class OINODbApi extends OINOApi {
17
+ /** DB reference */
18
+ readonly db: OINODb
19
+
20
+ /** DB parameters reference */
21
+ readonly dbParams: OINOApiParams
22
+
23
+ /** DB datamodel reference */
24
+ dbDatamodel: OINODbDataModel|null = null
25
+
26
+ /**
27
+ * Constructor of API object.
28
+ * NOTE! OINODb.initDatamodel must be called if created manually instead of the factory.
29
+ *
30
+ * @param db database for the API
31
+ * @param params parameters for the API
32
+ *
33
+ */
34
+ constructor (db: OINODb, params:OINOApiParams) {
35
+ super(db, params)
36
+ if (!params.tableName) {
37
+ throw new Error(OINO_ERROR_PREFIX + ": OINOApiParams needs to define a table name!")
38
+ }
39
+ this.db = db
40
+ this.dbParams = params
41
+
42
+ }
43
+
44
+ initializeDatamodel(datamodel: OINODbDataModel) {
45
+ this.dbDatamodel = datamodel
46
+ this.datamodel = datamodel
47
+ this.initialized = true
48
+ }
49
+
50
+ private _validateRow(result:OINOApiResult, row:OINODataRow, requirePrimaryKey:boolean):void {
51
+ let field:OINODataField
52
+ for (let i=0; i<this.dbDatamodel!.fields.length; i++) {
53
+ field = this.dbDatamodel!.fields[i]
54
+ const val:OINODataCell = row[i]
55
+ if ((val === null) && ((field.fieldParams.isNotNull)||(field.fieldParams.isPrimaryKey))) { // null is a valid SQL value except if it's not allowed
56
+ result.setError(405, "Field '" + field.name + "' is not allowed to be NULL!", "ValidateRowValues")
57
+
58
+ } else if ((val === undefined) && (requirePrimaryKey) && (field.fieldParams.isPrimaryKey) && (!field.fieldParams.isAutoInc)) {
59
+ result.setError(405, "Primary key '" + field.name + "' is not autoinc and missing from the data!", "ValidateRowValues")
60
+
61
+ } else if ((val !== undefined) && (this.dbParams.failOnUpdateOnAutoinc) && (field.fieldParams.isAutoInc)) {
62
+ result.setError(405, "Autoinc field '" + field.name + "' can't be updated!", "ValidateRowValues")
63
+
64
+ } else {
65
+ if ((field instanceof OINOStringDataField) && ((field.maxLength > 0))){
66
+ const str_val = val?.toString() || ""
67
+ if (str_val.length > field.maxLength) {
68
+ if (this.dbParams.failOnOversizedValues) {
69
+ result.setError(405, "Field '" + field.name + "' length (" + str_val.length + ") exceeds maximum (" + field.maxLength + ") and can't be set!", "ValidateRowValues")
70
+ } else {
71
+ result.addWarning("Field '" + field.name + "' length (" + str_val.length + ") exceeds maximum (" + field.maxLength + ") and might truncate or fail.", "ValidateRowValues")
72
+ }
73
+ }
74
+ }
75
+
76
+ }
77
+ }
78
+ //logDebug("OINODbApi.validateHttpValues", {result:result})
79
+ }
80
+
81
+ private _parseData(httpResult:OINOApiResult, request:OINOApiRequest):OINODataRow[] {
82
+ let rows:OINODataRow[] = []
83
+ const data = request.rowData ?? request.body
84
+ try {
85
+ if (Array.isArray(data)) {
86
+ rows = data as OINODataRow[]
87
+ } else if (data != null) {
88
+ rows = OINOParser.createRows(this.datamodel!, data, request.requestType, request.multipartBoundary)
89
+ }
90
+
91
+ } catch (e:any) {
92
+ httpResult.setError(400, "Invalid data: " + e.message, "DoRequest")
93
+ }
94
+ return rows
95
+ }
96
+
97
+ private async _doGet(result:OINOApiResult, rowId:string, request:OINOApiRequest):Promise<void> {
98
+ let sql:string = ""
99
+ try {
100
+ sql = this.dbDatamodel!.printSqlSelect(rowId, request.queryParams || {})
101
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "_doGet", "Print SQL", {sql:sql})
102
+ const sql_res:OINODataSet = await this.db.sqlSelect(sql)
103
+ if (sql_res.success == false) {
104
+ result.setError(500, sql_res.statusText, "DoGet")
105
+ if (this._debugOnError) {
106
+ result.addDebug("OINO GET SQL [" + sql + "]", "DoPut")
107
+ }
108
+ } else {
109
+ result.data = new OINOModelSet(this.datamodel!, sql_res, request.queryParams)
110
+ }
111
+ } catch (e:any) {
112
+ result.setError(500, "Unhandled exception in doGet: " + e.message, "DoGet")
113
+ OINOLog.exception("@oino-ts/db", "OINODbApi", "_doGet", "exception in get request", {message:e.message, stack:e.stack})
114
+ if (this._debugOnError) {
115
+ result.addDebug("OINO GET SQL [" + sql + "]", "DoGet")
116
+ }
117
+ }
118
+ }
119
+
120
+ private async _doPost(result:OINOApiResult, rows:OINODataRow[], request:OINOApiRequest):Promise<void> {
121
+ let sql:string = ""
122
+ try {
123
+ for (let i=0; i<rows.length; i++) {
124
+ this._validateRow(result, rows[i], this.dbParams.failOnInsertWithoutKey||false)
125
+ if (result.success) {
126
+ sql += this.dbDatamodel!.printSqlInsert(rows[i])
127
+
128
+ } else if (this.dbParams.failOnAnyInvalidRows == false) {
129
+ result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
130
+ }
131
+ }
132
+ if ((sql == "") && result.success) {
133
+ result.setError(405, "No valid rows for POST!", "DoPost")
134
+
135
+ } else if (result.success) {
136
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "_doPost", "Print SQL", {sql:sql})
137
+ const sql_res:OINODataSet = await this.db.sqlExec(sql)
138
+ if (sql_res.success == false) {
139
+ result.setError(500, sql_res.statusText, "DoPost")
140
+ if (this._debugOnError) {
141
+ result.addDebug("OINO POST MESSAGES [" + sql_res.statusText + "]", "DoPost")
142
+ result.addDebug("OINO POST SQL [" + sql + "]", "DoPost")
143
+ }
144
+ } else if (this.dbParams.returnInsertedIds) {
145
+ result.data = new OINOModelSet(this.datamodel!, sql_res, request.queryParams) // return the inserted ids as data
146
+ }
147
+ }
148
+ } catch (e:any) {
149
+ result.setError(500, "Unhandled exception in doPost: " + e.message, "DoPost")
150
+ OINOLog.exception("@oino-ts/db", "OINODbApi", "_doPost", "exception in post request", {message:e.message, stack:e.stack})
151
+ if (this._debugOnError) {
152
+ result.addDebug("OINO POST SQL [" + sql + "]", "DoPost")
153
+ }
154
+ }
155
+ }
156
+
157
+ private async _doPut(result:OINOApiResult, id:string|null, rows:OINODataRow[]):Promise<void> {
158
+ let sql:string = ""
159
+ try {
160
+ // this._validateRowValues(result, row, false)
161
+ for (let i=0; i<rows.length; i++) {
162
+ const row_id = id || OINOConfig.printOINOId(this.dbDatamodel!.getRowPrimarykeyValues(rows[i], this.hashid != null))
163
+ this._validateRow(result, rows[i], this.dbParams.failOnInsertWithoutKey||false)
164
+ if (result.success) {
165
+ sql += this.dbDatamodel!.printSqlUpdate(row_id, rows[i])
166
+
167
+ } else if (this.dbParams.failOnAnyInvalidRows == false) {
168
+ result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
169
+ }
170
+ }
171
+ if ((sql == "") && result.success) {
172
+ result.setError(405, "No valid rows for PUT!", "DoPut") // only set error if there are multiple rows and no valid sql was created
173
+
174
+ } else if (result.success) {
175
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "_doPut", "Print SQL", {sql:sql})
176
+ const sql_res:OINODataSet = await this.db.sqlExec(sql)
177
+ if (sql_res.success == false) {
178
+ result.setError(500, sql_res.statusText, "DoPut")
179
+ if (this._debugOnError) {
180
+ result.addDebug("OINO PUT MESSAGES [" + sql_res.statusText + "]", "DoPut")
181
+ result.addDebug("OINO PUT SQL [" + sql + "]", "DoPut")
182
+ }
183
+ }
184
+ }
185
+ } catch (e:any) {
186
+ result.setError(500, "Unhandled exception: " + e.message, "DoPut")
187
+ OINOLog.exception("@oino-ts/db", "OINODbApi", "_doPut", "exception in put request", {message:e.message, stack:e.stack})
188
+ if (this._debugOnError) {
189
+ result.addDebug("OINO PUT SQL [" + sql + "]", "DoPut")
190
+ }
191
+ }
192
+ }
193
+
194
+ private async _doDelete(result:OINOApiResult, id:string|null, rows:OINODataRow[]|null):Promise<void> {
195
+ let sql:string = ""
196
+ try {
197
+ if (rows != null) {
198
+ for (let i=0; i<rows.length; i++) {
199
+ const row_id = OINOConfig.printOINOId(this.dbDatamodel!.getRowPrimarykeyValues(rows[i], this.hashid != null))
200
+ if (row_id) {
201
+ sql += this.dbDatamodel!.printSqlDelete(row_id)
202
+ } else if (this.dbParams.failOnAnyInvalidRows == false) {
203
+ result.setOk() // individual rows may fail and will just be messages in response similar to executing multiple sql statements
204
+ }
205
+ }
206
+ } else if (id) {
207
+ sql = this.dbDatamodel!.printSqlDelete(id)
208
+ }
209
+ if ((sql == "") && result.success) {
210
+ result.setError(405, "No valid rows for DELETE!", "DoDelete") // only set error if there are multiple rows and no valid sql was created
211
+
212
+ } else if (result.success) {
213
+
214
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "_doDelete", "Print SQL", {sql:sql})
215
+ const sql_res:OINODataSet = await this.db.sqlExec(sql)
216
+ if (sql_res.success == false) {
217
+ result.setError(500, sql_res.statusText, "DoDelete")
218
+ if (this._debugOnError) {
219
+ result.addDebug("OINO DELETE MESSAGES [" + sql_res.statusText + "]", "DoDelete")
220
+ result.addDebug("OINO DELETE SQL [" + sql + "]", "DoDelete")
221
+ }
222
+ }
223
+ }
224
+ } catch (e:any) {
225
+ result.setError(500, "Unhandled exception: " + e.message, "DoDelete")
226
+ OINOLog.exception("@oino-ts/db", "OINODbApi", "_doDelete", "exception in delete request", {message:e.message, stack:e.stack})
227
+ if (this._debugOnError) {
228
+ result.addDebug("OINO DELETE SQL [" + sql + "]", "DoDelete")
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Method for handling a HTTP REST request with GET, POST, PUT, DELETE corresponding to
235
+ * SQL select, insert, update and delete.
236
+ *
237
+ * @param request OINO HTTP request object containing all parameters of the REST request
238
+ * @param rowId URL id of the REST request
239
+ * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
240
+ * @param queryParams SQL parameters for the REST request
241
+ *
242
+ */
243
+ async doHttpRequest(request: OINOHttpRequest, rowId:string, rowData:OINOApiData, queryParams:OINOQueryParams):Promise<OINOApiResult> {
244
+ const api_request = OINOApiRequest.fromHttpRequest(request, rowId, rowData, queryParams)
245
+ return this.doApiRequest(api_request)
246
+ }
247
+ /**
248
+ * Method for handling a HTTP REST request with GET, POST, PUT, DELETE corresponding to
249
+ * SQL select, insert, update and delete.
250
+ *
251
+ * @param method HTTP method of the REST request
252
+ * @param rowId URL id of the REST request
253
+ * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
254
+ * @param queryParams SQL parameters for the REST request
255
+ * @param contentType content type of the HTTP body data, default is JSON
256
+ *
257
+ */
258
+ async doRequest(method:string, rowId:string, rowData:OINOApiData, queryParams:OINOQueryParams, contentType:OINOContentType = OINOContentType.json):Promise<OINOApiResult> {
259
+ return this.doApiRequest(new OINOApiRequest({method: method, rowId: rowId, rowData: rowData, queryParams: queryParams, requestType: contentType}))
260
+ }
261
+
262
+ async doApiRequest(request:OINOApiRequest):Promise<OINOApiResult> {
263
+ if (this.initialized == false) {
264
+ throw new Error(OINO_ERROR_PREFIX + ": API is not initialized yet!")
265
+ }
266
+ OINOBenchmark.startMetric("OINODbApi", "doRequest." + request.method)
267
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "doRequest", "Request", {method:request.method, id:request.rowId, data:request.rowData})
268
+ let result:OINOApiResult = new OINOApiResult(request)
269
+ let rows:OINODataRow[] = []
270
+ if ((request.method == "POST") || (request.method == "PUT")) {
271
+ rows = this._parseData(result, request)
272
+ }
273
+ if (request.method == "GET") {
274
+ await this._doGet(result, request.rowId, request)
275
+
276
+ } else if (request.method == "PUT") {
277
+ if (!request.rowId) {
278
+ result.setError(400, "HTTP PUT method requires an URL ID for the row that is updated!", "DoRequest")
279
+
280
+ } else if (rows.length != 1) {
281
+ result.setError(400, "HTTP PUT method requires exactly one row in the body data!", "DoRequest")
282
+
283
+ } else {
284
+ try {
285
+ await this._doPut(result, request.rowId, rows)
286
+
287
+ } catch (e:any) {
288
+ result.setError(500, "Unhandled exception in HTTP PUT doRequest: " + e.message, "DoRequest")
289
+ }
290
+ }
291
+ } else if (request.method == "POST") {
292
+ if (request.rowId) {
293
+ result.setError(400, "HTTP POST method must not have an URL ID as it does not target an existing row but creates a new one!", "DoRequest")
294
+
295
+ } else if (rows.length == 0) {
296
+ result.setError(400, "HTTP POST method requires at least one row in the body data!", "DoRequest")
297
+
298
+ } else {
299
+ try {
300
+ await this._doPost(result, rows, request)
301
+
302
+ } catch (e:any) {
303
+ result.setError(500, "Unhandled exception in HTTP POST doRequest: " + e.message, "DoRequest")
304
+ }
305
+ }
306
+ } else if (request.method == "DELETE") {
307
+ if (!request.rowId) {
308
+ result.setError(400, "HTTP DELETE method requires an id!", "DoRequest")
309
+
310
+ } else {
311
+ try {
312
+ await this._doDelete(result, request.rowId, null)
313
+
314
+ } catch (e:any) {
315
+ result.setError(500, "Unhandled exception in HTTP DELETE doRequest: " + e.message, "DoRequest")
316
+ }
317
+ }
318
+ } else {
319
+ result.setError(405, "Unsupported HTTP method '" + request.method + "' for REST request", "DoRequest")
320
+ }
321
+
322
+ OINOBenchmark.endMetric("OINODbApi", "doRequest." + request.method, result.status != 500)
323
+
324
+ return Promise.resolve(result)
325
+ }
326
+
327
+ /**
328
+ * Method for handling a HTTP REST request with batch update using PUT or DELETE methods.
329
+ *
330
+ * @param method HTTP method of the REST request
331
+ * @param rowId URL id of the REST request
332
+ * @param rowData HTTP body data as either serialized string or unserialized JS object or OINODataRow-array or Buffer/Uint8Array binary data
333
+ *
334
+ */
335
+ async doBatchUpdate(method:string, rowId:string, rowData:OINOApiData, queryParams?: OINOQueryParams):Promise<OINOApiResult> {
336
+ return this.doApiRequest(new OINOApiRequest({ method: method, rowId: rowId, rowData: rowData, queryParams: queryParams }))
337
+ }
338
+ /**
339
+ * Method for handling a HTTP REST request with batch update using PUT or DELETE methods.
340
+ *
341
+ * @param request HTTP URL parameters as key-value-pairs
342
+ *
343
+ */
344
+ async doBatchApiRequest(request:OINOApiRequest):Promise<OINOApiResult> {
345
+ OINOLog.debug("@oino-ts/db", "OINODbApi", "doBatchUpdate", "Request", {request:request, data:request.rowData})
346
+ let result:OINOApiResult = new OINOApiResult(request)
347
+ if ((request.method != "PUT") && (request.method != "DELETE")) {
348
+ result.setError(500, "Batch update only supports PUT and DELETE methods!", "DoBatchUpdate")
349
+ return Promise.resolve(result)
350
+ }
351
+ OINOBenchmark.startMetric("OINODbApi", "doBatchUpdate." + request.method)
352
+ const rows:OINODataRow[] = [] = this._parseData(result, request)
353
+ if (request.method == "PUT") {
354
+
355
+ try {
356
+ await this._doPut(result, null, rows)
357
+
358
+ } catch (e:any) {
359
+ result.setError(500, "Unhandled exception in HTTP PUT doRequest: " + e.message, "DoBatchUpdate")
360
+ }
361
+
362
+ } else if (request.method == "DELETE") {
363
+ try {
364
+ await this._doDelete(result, null, rows)
365
+
366
+ } catch (e:any) {
367
+ result.setError(500, "Unhandled exception in HTTP DELETE doRequest: " + e.message, "DoBatchUpdate")
368
+ }
369
+ }
370
+ OINOBenchmark.endMetric("OINODbApi", "doBatchUpdate." + request.method, result.status != 500)
371
+ return Promise.resolve(result)
372
+ }
373
+
374
+ /**
375
+ * Method to check if a field is included in the API params.
376
+ *
377
+ * @param fieldName name of the field
378
+ *
379
+ */
380
+
381
+ public isFieldIncluded(fieldName:string):boolean {
382
+ const params = this.params
383
+ return (
384
+ ((params.excludeFieldPrefix == undefined) || (params.excludeFieldPrefix == "") || (fieldName.startsWith(params.excludeFieldPrefix) == false)) &&
385
+ ((params.excludeFields == undefined) || (params.excludeFields.length == 0) || (params.excludeFields.indexOf(fieldName) < 0)) &&
386
+ ((params.includeFields == undefined) || (params.includeFields.length == 0) || (params.includeFields.indexOf(fieldName) >= 0))
387
+ )
388
+ }
389
+
668
390
  }