@oino-ts/db 0.0.16 → 0.0.18

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.
@@ -34,11 +34,13 @@ class OINODbModelSet {
34
34
  }
35
35
  _encodeAndHashFieldValue(field, value, contentType, primaryKeyValues, rowIdSeed) {
36
36
  let result;
37
- if (field.fieldParams.isPrimaryKey) {
37
+ if (field.fieldParams.isPrimaryKey || field.fieldParams.isForeignKey) {
38
38
  if (value && (field instanceof index_js_1.OINONumberDataField) && (this.datamodel.api.hashid)) {
39
39
  value = this.datamodel.api.hashid.encode(value, rowIdSeed);
40
40
  }
41
- primaryKeyValues.push(value || "");
41
+ if (field.fieldParams.isPrimaryKey) {
42
+ primaryKeyValues.push(value || "");
43
+ }
42
44
  }
43
45
  result = index_js_1.OINOStr.encode(value, contentType);
44
46
  return result;
@@ -54,13 +56,13 @@ class OINODbModelSet {
54
56
  const f = fields[i];
55
57
  let value = f.serializeCell(row[i]);
56
58
  if (value === undefined) {
57
- index_js_1.OINOLog.info("OINODbModelSet._writeRowJson: undefined value skipped", { field_name: f.name });
59
+ // OINOLog.info("OINODbModelSet._writeRowJson: undefined value skipped", {field_name:f.name})
58
60
  }
59
61
  else if (value === null) {
60
62
  json_row += "," + index_js_1.OINOStr.encode(f.name, index_js_1.OINOContentType.json) + ":null";
61
63
  }
62
64
  else {
63
- let is_hashed = f.fieldParams.isPrimaryKey && (f instanceof index_js_1.OINONumberDataField) && (this.datamodel.api.hashid != null);
65
+ let is_hashed = (f.fieldParams.isPrimaryKey || f.fieldParams.isForeignKey) && (f instanceof index_js_1.OINONumberDataField) && (this.datamodel.api.hashid != null);
64
66
  let is_value = (f instanceof index_js_1.OINOBooleanDataField) || ((f instanceof index_js_1.OINONumberDataField) && !is_hashed);
65
67
  value = this._encodeAndHashFieldValue(f, value, index_js_1.OINOContentType.json, primary_key_values, f.name + " " + row_id_seed);
66
68
  if (is_value) {
@@ -92,7 +92,7 @@ class OINODbSqlFilter {
92
92
  return new OINODbSqlFilter(OINODbSqlFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1), OINODbSqlFilter.parse(boolean_parts[2]));
93
93
  }
94
94
  else {
95
- throw new Error(index_js_1.OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'");
95
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'"); // invalid filter could be a security risk, stop processing
96
96
  }
97
97
  }
98
98
  }
@@ -158,20 +158,24 @@ class OINODbSqlFilter {
158
158
  result += this._leftSide.toSql(dataModel);
159
159
  }
160
160
  else {
161
- result += dataModel.api.db.printSqlColumnname(this._leftSide);
162
161
  field = dataModel.findFieldByName(this._leftSide);
162
+ if (!field) {
163
+ index_js_1.OINOLog.error("OINODbSqlFilter.toSql: Invalid field!", { field: this._leftSide });
164
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid field '" + this._leftSide + "'"); // invalid field name could be a security risk, stop processing
165
+ }
166
+ result += dataModel.api.db.printSqlColumnname(field?.name || this._leftSide);
163
167
  }
164
168
  result += this._operatorToSql();
165
169
  if (this._rightSide instanceof OINODbSqlFilter) {
166
170
  result += this._rightSide.toSql(dataModel);
167
171
  }
168
172
  else {
169
- if (field) {
170
- result += field.printCellAsSqlValue(this._rightSide);
171
- }
172
- else {
173
- result += this._rightSide;
173
+ const value = field.deserializeCell(this._rightSide);
174
+ if (!value) {
175
+ index_js_1.OINOLog.error("OINODbSqlFilter.toSql: Invalid value!", { value: value });
176
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid value '" + value + "'"); // invalid value could be a security risk, stop processing
174
177
  }
178
+ result += field.printCellAsSqlValue(value);
175
179
  }
176
180
  // OINOLog.debug("OINOFilter.toSql", {result:result})
177
181
  return "(" + result + ")";
@@ -248,17 +252,19 @@ class OINODbSqlOrder {
248
252
  let result = "";
249
253
  for (let i = 0; i < this._columns.length; i++) {
250
254
  const field = dataModel.findFieldByName(this._columns[i]);
251
- if (field) {
252
- if (result) {
253
- result += ",";
254
- }
255
- result += dataModel.api.db.printSqlColumnname(field.name) + " ";
256
- if (this._descending[i]) {
257
- result += "DESC";
258
- }
259
- else {
260
- result += "ASC";
261
- }
255
+ if (!field) {
256
+ index_js_1.OINOLog.error("OINODbSqlOrder.toSql: Invalid field!", { field: this._columns[i] });
257
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlOrder.toSql - Invalid field '" + this._columns[i] + "'"); // invalid field name could be a security risk, stop processing
258
+ }
259
+ if (result) {
260
+ result += ",";
261
+ }
262
+ result += dataModel.api.db.printSqlColumnname(field.name) + " ";
263
+ if (this._descending[i]) {
264
+ result += "DESC";
265
+ }
266
+ else {
267
+ result += "ASC";
262
268
  }
263
269
  }
264
270
  // OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
@@ -271,16 +277,18 @@ exports.OINODbSqlOrder = OINODbSqlOrder;
271
277
  *
272
278
  */
273
279
  class OINODbSqlLimit {
274
- static _orderColumnRegex = /^(\d+)?$/i;
280
+ static _limitRegex = /^(\d+)(\spage\s)?(\d+)?$/i;
275
281
  _limit;
282
+ _page;
276
283
  /**
277
284
  * Constructor for `OINODbSqlLimit`.
278
285
  *
279
286
  * @param limit maximum number of items to return
280
287
  *
281
288
  */
282
- constructor(limit) {
289
+ constructor(limit, page = -1) {
283
290
  this._limit = limit;
291
+ this._page = page;
284
292
  }
285
293
  /**
286
294
  * Constructor for `OINODbSqlLimit` as parser of http parameter.
@@ -289,10 +297,14 @@ class OINODbSqlLimit {
289
297
  *
290
298
  */
291
299
  static parse(limitString) {
292
- try {
293
- return new OINODbSqlLimit(Number.parseInt(limitString));
300
+ let match = OINODbSqlLimit._limitRegex.exec(limitString);
301
+ if ((match != null) && (match.length == 4)) {
302
+ return new OINODbSqlLimit(Number.parseInt(match[1]), Number.parseInt(match[3]));
294
303
  }
295
- catch {
304
+ else if (match != null) {
305
+ return new OINODbSqlLimit(Number.parseInt(match[1]));
306
+ }
307
+ else {
296
308
  return new OINODbSqlLimit(-1);
297
309
  }
298
310
  }
@@ -314,6 +326,9 @@ class OINODbSqlLimit {
314
326
  return "";
315
327
  }
316
328
  let result = this._limit.toString();
329
+ if (this._page > 0) {
330
+ result += " OFFSET " + (this._limit * (this._page - 1) + 1).toString();
331
+ }
317
332
  return result;
318
333
  }
319
334
  }
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ /*
3
+ * This Source Code Form is subject to the terms of the Mozilla Public
4
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.OINODbSqlLimit = exports.OINODbSqlOrder = exports.OINODbSqlFilter = exports.OINODbSqlComparison = exports.OINODbSqlBooleanOperation = void 0;
9
+ const index_js_1 = require("./index.js");
10
+ /**
11
+ * Supported logical conjunctions in filter predicates.
12
+ * @enum
13
+ */
14
+ var OINODbSqlBooleanOperation;
15
+ (function (OINODbSqlBooleanOperation) {
16
+ OINODbSqlBooleanOperation["and"] = "and";
17
+ OINODbSqlBooleanOperation["or"] = "or";
18
+ OINODbSqlBooleanOperation["not"] = "not";
19
+ })(OINODbSqlBooleanOperation || (exports.OINODbSqlBooleanOperation = OINODbSqlBooleanOperation = {}));
20
+ /**
21
+ * Supported logical conjunctions in filter predicates.
22
+ * @enum
23
+ */
24
+ var OINODbSqlComparison;
25
+ (function (OINODbSqlComparison) {
26
+ OINODbSqlComparison["lt"] = "lt";
27
+ OINODbSqlComparison["le"] = "le";
28
+ OINODbSqlComparison["eq"] = "eq";
29
+ OINODbSqlComparison["ge"] = "ge";
30
+ OINODbSqlComparison["gt"] = "gt";
31
+ OINODbSqlComparison["like"] = "like";
32
+ })(OINODbSqlComparison || (exports.OINODbSqlComparison = OINODbSqlComparison = {}));
33
+ /**
34
+ * Class for recursively parsing of filters and printing them as SQL conditions.
35
+ * Supports three types of statements
36
+ * - comparison: (field)-lt|le|eq|ge|gt|like(value)
37
+ * - negation: -not(filter)
38
+ * - conjunction/disjunction: (filter)-and|or(filter)
39
+ * Supported conditions are comparisons (<, <=, =, >=, >) and substring match (LIKE).
40
+ *
41
+ */
42
+ class OINODbSqlFilter {
43
+ static _booleanOperationRegex = /^\s?\-(and|or)\s?$/i;
44
+ static _negationRegex = /^-(not|)\((.+)\)$/i;
45
+ static _filterComparisonRegex = /^\(([^'"\(\)]+)\)\s?\-(lt|le|eq|ge|gt|like)\s?\(([^'"\(\)]+)\)$/i;
46
+ _leftSide;
47
+ _rightSide;
48
+ _operator;
49
+ /**
50
+ * Constructor of `OINODbSqlFilter`
51
+ * @param leftSide left side of the filter, either another filter or a column name
52
+ * @param operation operation of the filter, either `OINODbSqlComparison` or `OINODbSqlBooleanOperation`
53
+ * @param rightSide right side of the filter, either another filter or a value
54
+ */
55
+ constructor(leftSide, operation, rightSide) {
56
+ if (!(((operation === null) && (leftSide == "") && (rightSide == "")) ||
57
+ ((operation !== null) && (Object.values(OINODbSqlComparison).includes(operation)) && (typeof (leftSide) == "string") && (leftSide != "") && (typeof (rightSide) == "string") && (rightSide != "")) ||
58
+ ((operation == OINODbSqlBooleanOperation.not) && (leftSide == "") && (rightSide instanceof OINODbSqlFilter)) ||
59
+ (((operation == OINODbSqlBooleanOperation.and) || (operation == OINODbSqlBooleanOperation.or)) && (leftSide instanceof OINODbSqlFilter) && (rightSide instanceof OINODbSqlFilter)))) {
60
+ index_js_1.OINOLog.debug("Unsupported OINODbSqlFilter format!", { leftSide: leftSide, operation: operation, rightSide: rightSide });
61
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": Unsupported OINODbSqlFilter format!");
62
+ }
63
+ this._leftSide = leftSide;
64
+ this._operator = operation;
65
+ this._rightSide = rightSide;
66
+ }
67
+ /**
68
+ * Constructor for `OINOFilter` as parser of http parameter.
69
+ *
70
+ * @param filterString string representation of filter from HTTP-request
71
+ *
72
+ */
73
+ static parse(filterString) {
74
+ // OINOLog_debug("OINOFilter.constructor", {filterString:filterString})
75
+ if (!filterString) {
76
+ return new OINODbSqlFilter("", null, "");
77
+ }
78
+ else {
79
+ let match = OINODbSqlFilter._filterComparisonRegex.exec(filterString);
80
+ if (match != null) {
81
+ return new OINODbSqlFilter(match[1], match[2].toLowerCase(), match[3]);
82
+ }
83
+ else {
84
+ let match = OINODbSqlFilter._negationRegex.exec(filterString);
85
+ if (match != null) {
86
+ return new OINODbSqlFilter("", OINODbSqlBooleanOperation.not, OINODbSqlFilter.parse(match[3]));
87
+ }
88
+ else {
89
+ let boolean_parts = index_js_1.OINOStr.splitByBrackets(filterString, true, false, '(', ')');
90
+ // OINOLog_debug("OINOFilter.constructor", {boolean_parts:boolean_parts})
91
+ if (boolean_parts.length == 3 && (boolean_parts[1].match(OINODbSqlFilter._booleanOperationRegex))) {
92
+ return new OINODbSqlFilter(OINODbSqlFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1), OINODbSqlFilter.parse(boolean_parts[2]));
93
+ }
94
+ else {
95
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'"); // invalid filter could be a security risk, stop processing
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ /**
102
+ * Construct a new `OINOFilter` as combination of (boolean and/or) of two filters.
103
+ *
104
+ * @param leftSide left side to combine
105
+ * @param operation boolean operation to use in combination
106
+ * @param rightSide right side to combine
107
+ *
108
+ */
109
+ static combine(leftSide, operation, rightSide) {
110
+ if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
111
+ return new OINODbSqlFilter(leftSide, operation, rightSide);
112
+ }
113
+ else if ((leftSide) && (!leftSide.isEmpty())) {
114
+ return leftSide;
115
+ }
116
+ else if ((rightSide) && (!rightSide.isEmpty())) {
117
+ return rightSide;
118
+ }
119
+ else {
120
+ return undefined;
121
+ }
122
+ }
123
+ _operatorToSql() {
124
+ switch (this._operator) {
125
+ case "and": return " AND ";
126
+ case "or": return " OR ";
127
+ case "not": return "NOT ";
128
+ case "lt": return " < ";
129
+ case "le": return " <= ";
130
+ case "eq": return " = ";
131
+ case "ge": return " >= ";
132
+ case "gt": return " > ";
133
+ case "like": return " LIKE ";
134
+ }
135
+ return " ";
136
+ }
137
+ /**
138
+ * Does filter contain any valid conditions.
139
+ *
140
+ */
141
+ isEmpty() {
142
+ return (this._leftSide == "") && (this._operator == null) && (this._rightSide == "");
143
+ }
144
+ /**
145
+ * Print filter as SQL condition based on the datamodel of the API.
146
+ *
147
+ * @param dataModel data model (and database) to use for formatting of values
148
+ *
149
+ */
150
+ toSql(dataModel) {
151
+ // OINOLog.debug("OINOFilter.toSql", {_leftSide:this._leftSide, _operator:this._operator, _rightSide:this._rightSide})
152
+ if (this.isEmpty()) {
153
+ return "";
154
+ }
155
+ let result = "";
156
+ let field = null;
157
+ if (this._leftSide instanceof OINODbSqlFilter) {
158
+ result += this._leftSide.toSql(dataModel);
159
+ }
160
+ else {
161
+ field = dataModel.findFieldByName(this._leftSide);
162
+ if (!field) {
163
+ index_js_1.OINOLog.error("OINODbSqlFilter.toSql: Invalid field!", { field: this._leftSide });
164
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid field '" + this._leftSide + "'"); // invalid field name could be a security risk, stop processing
165
+ }
166
+ result += dataModel.api.db.printSqlColumnname(field?.name || this._leftSide);
167
+ }
168
+ result += this._operatorToSql();
169
+ if (this._rightSide instanceof OINODbSqlFilter) {
170
+ result += this._rightSide.toSql(dataModel);
171
+ }
172
+ else {
173
+ const value = field.deserializeCell(this._rightSide);
174
+ if (!value) {
175
+ index_js_1.OINOLog.error("OINODbSqlFilter.toSql: Invalid value!", { value: value });
176
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid value '" + value + "'"); // invalid value could be a security risk, stop processing
177
+ }
178
+ result += field.printCellAsSqlValue(value);
179
+ }
180
+ // OINOLog.debug("OINOFilter.toSql", {result:result})
181
+ return "(" + result + ")";
182
+ }
183
+ }
184
+ exports.OINODbSqlFilter = OINODbSqlFilter;
185
+ /**
186
+ * Class for ordering select results on a number of columns.
187
+ *
188
+ */
189
+ class OINODbSqlOrder {
190
+ static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC)?\s*?$/i;
191
+ _columns;
192
+ _descending;
193
+ /**
194
+ * Constructor for `OINODbSqlOrder`.
195
+ *
196
+ * @param column_or_array single or array of columns to order on
197
+ * @param descending_or_array single or array of booleans if ordes is descending
198
+ *
199
+ */
200
+ constructor(column_or_array, descending_or_array) {
201
+ index_js_1.OINOLog.debug("OINODbSqlOrder.constructor", { columns: column_or_array, directions: descending_or_array });
202
+ if (Array.isArray(column_or_array)) {
203
+ this._columns = column_or_array;
204
+ }
205
+ else {
206
+ this._columns = [column_or_array];
207
+ }
208
+ if (Array.isArray(descending_or_array)) {
209
+ this._descending = descending_or_array;
210
+ }
211
+ else {
212
+ this._descending = [descending_or_array];
213
+ }
214
+ }
215
+ /**
216
+ * Constructor for `OINODbSqlOrder` as parser of http parameter.
217
+ *
218
+ * @param orderString string representation of ordering from HTTP-request
219
+ *
220
+ */
221
+ static parse(orderString) {
222
+ let columns = [];
223
+ let directions = [];
224
+ const column_strings = orderString.split(',');
225
+ for (let i = 0; i < column_strings.length; i++) {
226
+ let match = OINODbSqlOrder._orderColumnRegex.exec(column_strings[i]);
227
+ if (match != null) {
228
+ columns.push(match[1]);
229
+ directions.push((match[2] || "DESC").toUpperCase() == "DESC");
230
+ }
231
+ }
232
+ return new OINODbSqlOrder(columns, directions);
233
+ }
234
+ /**
235
+ * Does filter contain any valid conditions.
236
+ *
237
+ */
238
+ isEmpty() {
239
+ return (this._columns.length == 0);
240
+ }
241
+ /**
242
+ * Print order as SQL condition based on the datamodel of the API.
243
+ *
244
+ * @param dataModel data model (and database) to use for formatting of values
245
+ *
246
+ */
247
+ toSql(dataModel) {
248
+ if (this.isEmpty()) {
249
+ return "";
250
+ }
251
+ // OINOLog.debug("OINODbSqlOrder.toSql", {columns:this._columns, directions:this._directions})
252
+ let result = "";
253
+ for (let i = 0; i < this._columns.length; i++) {
254
+ const field = dataModel.findFieldByName(this._columns[i]);
255
+ if (!field) {
256
+ index_js_1.OINOLog.error("OINODbSqlOrder.toSql: Invalid field!", { field: this._columns[i] });
257
+ throw new Error(index_js_1.OINO_ERROR_PREFIX + ": OINODbSqlOrder.toSql - Invalid field '" + this._columns[i] + "'"); // invalid field name could be a security risk, stop processing
258
+ }
259
+ if (result) {
260
+ result += ",";
261
+ }
262
+ result += dataModel.api.db.printSqlColumnname(field.name) + " ";
263
+ if (this._descending[i]) {
264
+ result += "DESC";
265
+ }
266
+ else {
267
+ result += "ASC";
268
+ }
269
+ }
270
+ // OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
271
+ return result;
272
+ }
273
+ }
274
+ exports.OINODbSqlOrder = OINODbSqlOrder;
275
+ /**
276
+ * Class for limiting the number of results.
277
+ *
278
+ */
279
+ class OINODbSqlLimit {
280
+ static _limitRegex = /^(\d+)(\spage\s)?(\d+)?$/i;
281
+ _limit;
282
+ _page;
283
+ /**
284
+ * Constructor for `OINODbSqlLimit`.
285
+ *
286
+ * @param limit maximum number of items to return
287
+ * @param page page number to return starting from 1
288
+ *
289
+ */
290
+ constructor(limit, page = -1) {
291
+ this._limit = limit;
292
+ this._page = page;
293
+ }
294
+ /**
295
+ * Constructor for `OINODbSqlLimit` as parser of http parameter.
296
+ *
297
+ * @param limitString string representation of limit from HTTP-request
298
+ *
299
+ */
300
+ static parse(limitString) {
301
+ let match = OINODbSqlLimit._limitRegex.exec(limitString);
302
+ if ((match != null) && (match.length == 4)) {
303
+ return new OINODbSqlLimit(Number.parseInt(match[1]), Number.parseInt(match[3]));
304
+ }
305
+ else if (match != null) {
306
+ return new OINODbSqlLimit(Number.parseInt(match[1]));
307
+ }
308
+ else {
309
+ return new OINODbSqlLimit(-1);
310
+ }
311
+ }
312
+ /**
313
+ * Does filter contain any valid conditions.
314
+ *
315
+ */
316
+ isEmpty() {
317
+ return (this._limit <= 0);
318
+ }
319
+ /**
320
+ * Print order as SQL condition based on the datamodel of the API.
321
+ *
322
+ * @param dataModel data model (and database) to use for formatting of values
323
+ *
324
+ */
325
+ toSql(dataModel) {
326
+ if (this.isEmpty()) {
327
+ return "";
328
+ }
329
+ let result = this._limit.toString();
330
+ if (this._page > 0) {
331
+ result += " OFFSET " + (this._limit * (this._page - 1) + 1).toString();
332
+ }
333
+ return result;
334
+ }
335
+ }
336
+ exports.OINODbSqlLimit = OINODbSqlLimit;
package/dist/cjs/index.js CHANGED
@@ -36,12 +36,12 @@ Object.defineProperty(exports, "OINODb", { enumerable: true, get: function () {
36
36
  var OINODbDataSet_js_1 = require("./OINODbDataSet.js");
37
37
  Object.defineProperty(exports, "OINODbDataSet", { enumerable: true, get: function () { return OINODbDataSet_js_1.OINODbDataSet; } });
38
38
  Object.defineProperty(exports, "OINODbMemoryDataSet", { enumerable: true, get: function () { return OINODbDataSet_js_1.OINODbMemoryDataSet; } });
39
- var OINODbRequestParams_js_1 = require("./OINODbRequestParams.js");
40
- Object.defineProperty(exports, "OINODbSqlFilter", { enumerable: true, get: function () { return OINODbRequestParams_js_1.OINODbSqlFilter; } });
41
- Object.defineProperty(exports, "OINODbSqlOrder", { enumerable: true, get: function () { return OINODbRequestParams_js_1.OINODbSqlOrder; } });
42
- Object.defineProperty(exports, "OINODbSqlComparison", { enumerable: true, get: function () { return OINODbRequestParams_js_1.OINODbSqlComparison; } });
43
- Object.defineProperty(exports, "OINODbSqlLimit", { enumerable: true, get: function () { return OINODbRequestParams_js_1.OINODbSqlLimit; } });
44
- Object.defineProperty(exports, "OINODbSqlBooleanOperation", { enumerable: true, get: function () { return OINODbRequestParams_js_1.OINODbSqlBooleanOperation; } });
39
+ var OINODbSqlParams_js_1 = require("./OINODbSqlParams.js");
40
+ Object.defineProperty(exports, "OINODbSqlFilter", { enumerable: true, get: function () { return OINODbSqlParams_js_1.OINODbSqlFilter; } });
41
+ Object.defineProperty(exports, "OINODbSqlOrder", { enumerable: true, get: function () { return OINODbSqlParams_js_1.OINODbSqlOrder; } });
42
+ Object.defineProperty(exports, "OINODbSqlComparison", { enumerable: true, get: function () { return OINODbSqlParams_js_1.OINODbSqlComparison; } });
43
+ Object.defineProperty(exports, "OINODbSqlLimit", { enumerable: true, get: function () { return OINODbSqlParams_js_1.OINODbSqlLimit; } });
44
+ Object.defineProperty(exports, "OINODbSqlBooleanOperation", { enumerable: true, get: function () { return OINODbSqlParams_js_1.OINODbSqlBooleanOperation; } });
45
45
  var OINODbConfig_js_1 = require("./OINODbConfig.js");
46
46
  Object.defineProperty(exports, "OINODbConfig", { enumerable: true, get: function () { return OINODbConfig_js_1.OINODbConfig; } });
47
47
  var OINODbFactory_js_1 = require("./OINODbFactory.js");
@@ -20,4 +20,30 @@ export class OINODb {
20
20
  this._params = params;
21
21
  this.name = params.database;
22
22
  }
23
+ /**
24
+ * Print SQL select statement with DB specific formatting.
25
+ *
26
+ * @param tableName - The name of the table to select from.
27
+ * @param columnNames - The columns to be selected.
28
+ * @param whereCondition - The WHERE clause to filter the results.
29
+ * @param orderCondition - The ORDER BY clause to sort the results.
30
+ * @param limitCondition - The LIMIT clause to limit the number of results.
31
+ *
32
+ */
33
+ printSqlSelect(tableName, columnNames, whereCondition, orderCondition, limitCondition) {
34
+ let result = "SELECT " + columnNames + " FROM " + tableName;
35
+ // OINOLog.debug("OINODb.printSqlSelect", {tableName:tableName, columnNames:columnNames, whereCondition:whereCondition, orderCondition:orderCondition, limitCondition:limitCondition })
36
+ if (whereCondition != "") {
37
+ result += " WHERE " + whereCondition;
38
+ }
39
+ if (orderCondition != "") {
40
+ result += " ORDER BY " + orderCondition;
41
+ }
42
+ if (limitCondition != "") {
43
+ result += " LIMIT " + limitCondition;
44
+ }
45
+ result += ";";
46
+ // OINOLog.debug("OINODb.printSqlSelect", {result:result})
47
+ return result;
48
+ }
23
49
  }
@@ -3,9 +3,10 @@
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 { OINODbDataModel, OINOStringDataField, OINO_ERROR_PREFIX, OINODbModelSet, OINOBenchmark, OINODbFactory, OINODbConfig, OINOHtmlTemplate, OINONumberDataField } from "./index.js";
6
+ import { OINODbDataModel, OINOStringDataField, OINO_ERROR_PREFIX, OINODbModelSet, OINOBenchmark, OINODbConfig, OINOHtmlTemplate, OINONumberDataField } from "./index.js";
7
7
  import { OINOResult } from "@oino-ts/types";
8
8
  import { OINOHashid } from "@oino-ts/hashid";
9
+ import { OINOParser } from "@oino-ts/types";
9
10
  const API_EMPTY_PARAMS = { sqlParams: {} };
10
11
  /**
11
12
  * OINO API request result object with returned data and/or http status code/message and
@@ -35,7 +36,7 @@ export class OINODbApiResult extends OINOResult {
35
36
  * @param headers Headers to include in the response
36
37
  *
37
38
  */
38
- async createResponseFromResult(headers = {}) {
39
+ async getResponse(headers = {}) {
39
40
  let response = null;
40
41
  if (this.success && this.data) {
41
42
  const body = await this.data.writeString(this.params.responseType);
@@ -85,11 +86,13 @@ export class OINODbHtmlTemplate extends OINOHtmlTemplate {
85
86
  for (let i = 0; i < datamodel.fields.length; i++) {
86
87
  const f = datamodel.fields[i];
87
88
  let value = f.serializeCell(row[i]);
88
- if (f.fieldParams.isPrimaryKey) {
89
+ if (f.fieldParams.isPrimaryKey || f.fieldParams.isForeignKey) {
89
90
  if (value && (f instanceof OINONumberDataField) && (datamodel.api.hashid)) {
90
91
  value = datamodel.api.hashid.encode(value, f.name + " " + row_id_seed);
91
92
  }
92
- primary_key_values.push(value || "");
93
+ if (f.fieldParams.isPrimaryKey) {
94
+ primary_key_values.push(value || "");
95
+ }
93
96
  }
94
97
  // OINOLog.debug("renderFromDbData replace field value", {field:f.name, value:value })
95
98
  this.setVariableFromValue(f.name, value || "");
@@ -177,9 +180,10 @@ export class OINODbApi {
177
180
  //logDebug("OINODbApi.validateHttpValues", {result:result})
178
181
  }
179
182
  async _doGet(result, id, params) {
180
- const sql = this.datamodel.printSqlSelect(id, params.sqlParams || {});
181
- // OINOLog.debug("OINODbApi.doGet sql", {sql:sql})
183
+ let sql = "";
182
184
  try {
185
+ sql = this.datamodel.printSqlSelect(id, params.sqlParams || {});
186
+ // OINOLog.debug("OINODbApi.doGet sql", {sql:sql})
183
187
  const sql_res = await this.db.sqlSelect(sql);
184
188
  // OINOLog.debug("OINODbApi.doGet sql_res", {sql_res:sql_res})
185
189
  if (sql_res.hasErrors()) {
@@ -282,14 +286,16 @@ export class OINODbApi {
282
286
  let result = new OINODbApiResult(params);
283
287
  let rows = [];
284
288
  if ((method == "POST") || (method == "PUT")) {
285
- if (Array.isArray(body)) {
286
- rows = body;
287
- }
288
- else if (typeof (body) == "object") {
289
- rows = [OINODbFactory.createRowFromObject(this.datamodel, body)];
289
+ try {
290
+ if (Array.isArray(body)) {
291
+ rows = body;
292
+ }
293
+ else {
294
+ rows = OINOParser.createRows(this.datamodel, body, params);
295
+ }
290
296
  }
291
- else if (typeof (body) == "string") {
292
- rows = OINODbFactory.createRows(this.datamodel, body, params);
297
+ catch (e) {
298
+ result.setError(400, "Invalid data: " + e.message, "DoRequest");
293
299
  }
294
300
  // OINOLog.debug("OINODbApi.doRequest - OINODataRow rows", {rows:rows})
295
301
  }
@@ -3,6 +3,7 @@
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 { OINOLog, OINO_ERROR_PREFIX } from "./index.js";
6
7
  /**
7
8
  * Base class for a column of data responsible for appropriatelly serializing/deserializing the data.
8
9
  *
@@ -51,6 +52,9 @@ export class OINODbDataField {
51
52
  if (this.fieldParams.isPrimaryKey) {
52
53
  params += "PK ";
53
54
  }
55
+ if (this.fieldParams.isForeignKey) {
56
+ params += "FK ";
57
+ }
54
58
  if (this.fieldParams.isAutoInc) {
55
59
  params += "AUTOINC ";
56
60
  }
@@ -236,7 +240,12 @@ export class OINONumberDataField extends OINODbDataField {
236
240
  return null;
237
241
  }
238
242
  else {
239
- return Number.parseFloat(value);
243
+ const result = parseFloat(value);
244
+ if (isNaN(result)) {
245
+ OINOLog.error("OINODbSqlFilter.toSql: Invalid value!", { value: value });
246
+ throw new Error(OINO_ERROR_PREFIX + ": OINONumberDataField.deserializeCell - Invalid value '" + value + "'"); // incorrectly formatted data could be a security risk, abort processing
247
+ }
248
+ return result;
240
249
  }
241
250
  }
242
251
  }
@@ -284,7 +293,7 @@ export class OINOBlobDataField extends OINODbDataField {
284
293
  */
285
294
  deserializeCell(value) {
286
295
  if (value == null) {
287
- return new Buffer(0);
296
+ return Buffer.alloc(0);
288
297
  }
289
298
  else {
290
299
  return Buffer.from(value, 'base64'); // Blob-field data is base64 encoded and converted internally to UInt8Array / Buffer