@oino-ts/db 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  ```
23
23
 
24
24
  ### Register database and logger
25
- Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOConsoleLog.html) how to implement your own)
25
+ Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/types_src.OINOConsoleLog.html) how to implement your own)
26
26
 
27
27
  ```
28
28
  OINOLog.setLogger(new OINOConsoleLog())
@@ -30,13 +30,13 @@
30
30
  ```
31
31
 
32
32
  ### Create a database
33
- Creating a database connection [`OINODb`](https://pragmatta.github.io/oino-ts/classes/core_src_OINODb.OINODb.html) is done by passing [`OINODbParams`](https://pragmatta.github.io/oino-ts/types/core_src.OINODbParams.html) to the factory method. For [`OINODbBunSqlite`](https://pragmatta.github.io/oino-ts/classes/bunsqlite_OINODbBunSqlite.OINODbBunSqlite.html) that means a file url for the database file, for others network host, port, credentials etc.
33
+ Creating a database connection [`OINODb`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODb.html) is done by passing [`OINODbParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbParams.html) to the factory method. For [`OINODbBunSqlite`](https://pragmatta.github.io/oino-ts/classes/db_bunsqlite_src.OINODbBunSqlite.html) that means a file url for the database file, for others network host, port, credentials etc.
34
34
  ```
35
35
  const db:OINODb = await OINOFactory.createDb( { type: "OINODbBunSqlite", url: "file://../localDb/northwind.sqlite" } )
36
36
  ```
37
37
 
38
38
  ### Create an API
39
- From a database you can create an [`OINOApi`](https://pragmatta.github.io/oino-ts/classes/core_src_OINOApi.OINOApi.html) by passing [`OINOApiParams`](https://pragmatta.github.io/oino-ts/types/core_src.OINOApiParams.html) with table name and preferences to the factory method.
39
+ From a database you can create an [`OINOApi`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbApi.html) by passing [`OINOApiParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbApiParams.html) with table name and preferences to the factory method.
40
40
  ```
41
41
  const api_employees:OINOApi = await OINOFactory.createApi(db, { tableName: "Employees", excludeFields:["BirthDate"] })
42
42
  ```
@@ -49,7 +49,7 @@
49
49
  ```
50
50
 
51
51
  ### Write results back to HTTP Response
52
- The results for a GET request will contain [`OINOModelSet`](https://pragmatta.github.io/oino-ts/classes/core_src_OINOModelSet.OINOModelSet.html) data that can be written out as JSON or CSV as needed. For other requests result is just success or error with messages.
52
+ The results for a GET request will contain [`OINOModelSet`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbModelSet.html) data that can be written out as JSON or CSV as needed. For other requests result is just success or error with messages.
53
53
  ```
54
54
  return new Response(result.modelset.writeString(OINOContentType.json))
55
55
  ```
@@ -134,7 +134,7 @@
134
134
  To support tables with multipart primary keys OINO generates a composite key `_OINOID_` that is included in the result and can be used as the REST ID. For example in the example above table `OrderDetails` has two primary keys `OrderID` and `ProductID` making the `_OINOID_` of form `11077:99`.
135
135
 
136
136
  ## Power Of SQL
137
- Since OINO controls the SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOSqlFilter.html) and order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOSqlOrder.html) that are passed as HTTP request parameters. No more API development where you make unique API endpoints for each filter that fetch all data with original API and filter in backend code. Every API can be filtered when and as needed without unnessecary data tranfer and utilizing SQL indexing when available.
137
+ Since OINO controls the SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html) and order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.html) that are passed as HTTP request parameters. No more API development where you make unique API endpoints for each filter that fetch all data with original API and filter in backend code. Every API can be filtered when and as needed without unnessecary data tranfer and utilizing SQL indexing when available.
138
138
 
139
139
  ## Swagger Support
140
140
  Swagger is great as long as the definitions are updated and with OINO you can automatically get a Swagger definition including a data model schema.
@@ -149,7 +149,7 @@
149
149
  OINO is developped Typescript first but compiles to standard CommonJS and the NPM packages should work on either ESM / CommonJS. Checkout sample apps `readmeApp` (ESM) and `nodeApp` (CommonJS).
150
150
 
151
151
  ## HTMX support
152
- OINO is [htmx.org](https://htmx.org) friendly, allowing easy translation of [`OINODataRow`](https://pragmatta.github.io/oino-ts/types/core_src.OINODataRow.html) to HTML output using templates (cf. the [htmx sample app](https://github.com/pragmatta/oino-ts/tree/main/samples/htmxApp)).
152
+ OINO is [htmx.org](https://htmx.org) friendly, allowing easy translation of [`OINODataRow`](https://pragmatta.github.io/oino-ts/types/db_src.OINODataRow.html) to HTML output using templates (cf. the [htmx sample app](https://github.com/pragmatta/oino-ts/tree/main/samples/htmxApp)).
153
153
 
154
154
  ### Hashids
155
155
  Autoinc numeric id's are very pragmatic and fit well with OINO (e.g. using a form without primary key fields to insert new rows with database assigned ids). However it's not always sensible to share information about the sequence. Hashids solve this by masking the original values by encrypting the ids using AES-128 and some randomness. Length of the hashid can be chosen from 12-32 characters where longer ids provide more security. However this should not be considereded a cryptographic solution for keeping ids secret but rather making it infeasible to iterate all ids.
@@ -5,29 +5,102 @@
5
5
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.OINODbApi = exports.OINODbApiResult = void 0;
8
+ exports.OINODbApi = exports.OINODbHtmlTemplate = exports.OINODbApiResult = void 0;
9
9
  const index_js_1 = require("./index.js");
10
+ const types_1 = require("@oino-ts/types");
10
11
  const hashid_1 = require("@oino-ts/hashid");
12
+ const API_EMPTY_PARAMS = { sqlParams: {} };
11
13
  /**
12
14
  * OINO API request result object with returned data and/or http status code/message and
13
15
  * error / warning messages.
14
16
  *
15
17
  */
16
- class OINODbApiResult extends index_js_1.OINOResult {
18
+ class OINODbApiResult extends types_1.OINOResult {
19
+ /** DbApi request params */
20
+ params;
17
21
  /** Returned data if any */
18
22
  data;
19
23
  /**
20
24
  * Constructor of OINODbApiResult.
21
25
  *
26
+ * @param params DbApi request parameters
22
27
  * @param data result data
23
28
  *
24
29
  */
25
- constructor(data) {
30
+ constructor(params, data) {
26
31
  super();
32
+ this.params = params;
27
33
  this.data = data;
28
34
  }
35
+ /**
36
+ * Creates a HTTP Response from API results.
37
+ *
38
+ * @param headers Headers to include in the response
39
+ *
40
+ */
41
+ getResponse(headers = {}) {
42
+ let response = null;
43
+ if (this.success && this.data) {
44
+ response = new Response(this.data.writeString(this.params.responseType), { status: this.statusCode, statusText: this.statusMessage, headers: headers });
45
+ }
46
+ else {
47
+ response = new Response(JSON.stringify(this), { status: this.statusCode, statusText: this.statusMessage, headers: headers });
48
+ }
49
+ for (let i = 0; i < this.messages.length; i++) {
50
+ response.headers.set('X-OINO-MESSAGE-' + i, this.messages[i]);
51
+ }
52
+ return response;
53
+ }
29
54
  }
30
55
  exports.OINODbApiResult = OINODbApiResult;
56
+ /**
57
+ * Specialized HTML template that can render ´OINODbApiResult´.
58
+ *
59
+ */
60
+ class OINODbHtmlTemplate extends index_js_1.OINOHtmlTemplate {
61
+ /**
62
+ * Creates HTML Response from API modelset.
63
+ *
64
+ * @param modelset OINO API dataset
65
+ *
66
+ */
67
+ renderFromDbData(modelset) {
68
+ let html = "";
69
+ const dataset = modelset.dataset;
70
+ const datamodel = modelset.datamodel;
71
+ const api = modelset.datamodel.api;
72
+ const modified_index = datamodel.findFieldIndexByName(api.params.cacheModifiedField || "");
73
+ let last_modified = this.modified;
74
+ while (!dataset.isEof()) {
75
+ const row = dataset.getRow();
76
+ if (modified_index >= 0) {
77
+ last_modified = Math.max(last_modified, new Date(row[modified_index]).getTime());
78
+ }
79
+ let row_id_seed = datamodel.getRowPrimarykeyValues(row).join(' ');
80
+ let primary_key_values = [];
81
+ let html_row = this.template.replaceAll('###' + index_js_1.OINODbConfig.OINODB_ID_FIELD + '###', '###createHtmlFromData_temporary_oinoid###');
82
+ for (let i = 0; i < datamodel.fields.length; i++) {
83
+ const f = datamodel.fields[i];
84
+ let value = f.serializeCell(row[i]);
85
+ if (f.fieldParams.isPrimaryKey) {
86
+ if (value && (f instanceof index_js_1.OINONumberDataField) && (datamodel.api.hashid)) {
87
+ value = datamodel.api.hashid.encode(value, f.name + " " + row_id_seed);
88
+ }
89
+ primary_key_values.push(value || "");
90
+ }
91
+ // OINOLog.debug("renderFromDbData replace field value", {field:f.name, value:value })
92
+ html_row = html_row.replaceAll('###' + f.name + '###', index_js_1.OINOStr.encode(value, index_js_1.OINOContentType.html));
93
+ }
94
+ html_row = html_row.replaceAll('###createHtmlFromData_temporary_oinoid###', index_js_1.OINOStr.encode(index_js_1.OINODbConfig.printOINOId(primary_key_values), index_js_1.OINOContentType.html));
95
+ html += html_row + "\r\n";
96
+ dataset.next();
97
+ }
98
+ const result = new index_js_1.OINOHttpResult(html);
99
+ result.lastModified = last_modified;
100
+ return result;
101
+ }
102
+ }
103
+ exports.OINODbHtmlTemplate = OINODbHtmlTemplate;
31
104
  /**
32
105
  * API class with method to process HTTP REST requests.
33
106
  *
@@ -99,7 +172,7 @@ class OINODbApi {
99
172
  }
100
173
  async _doGet(result, id, params) {
101
174
  index_js_1.OINOBenchmark.start("doGet");
102
- const sql = this.datamodel.printSqlSelect(id, params.sqlParams);
175
+ const sql = this.datamodel.printSqlSelect(id, params.sqlParams || {});
103
176
  // OINOLog.debug("OINODbApi.doGet sql", {sql:sql})
104
177
  try {
105
178
  const sql_res = await this.db.sqlSelect(sql);
@@ -201,19 +274,31 @@ class OINODbApi {
201
274
  *
202
275
  * @param method HTTP verb (uppercase)
203
276
  * @param id URL id of the REST request
204
- * @param body HTTP body data as string
277
+ * @param body HTTP body data as either serialized string or unserialized JS object / OINODataRow-array
205
278
  * @param params HTTP URL parameters as key-value-pairs
206
279
  *
207
280
  */
208
- async doRequest(method, id, body, params) {
281
+ async doRequest(method, id, body, params = API_EMPTY_PARAMS) {
209
282
  index_js_1.OINOBenchmark.start("doRequest");
210
- let result = new OINODbApiResult();
211
- index_js_1.OINOLog.debug("OINODbApi.doRequest enter", { method: method, id: id, body: body, searchParams: params });
283
+ // OINOLog.debug("OINODbApi.doRequest enter", {method:method, id:id, body:body, params:params})
284
+ let result = new OINODbApiResult(params);
285
+ let rows = [];
286
+ if ((method == "POST") || (method == "PUT")) {
287
+ if (Array.isArray(body)) {
288
+ rows = body;
289
+ }
290
+ else if (typeof (body) == "object") {
291
+ rows = [index_js_1.OINODbFactory.createRowFromObject(this.datamodel, body)];
292
+ }
293
+ else if (typeof (body) == "string") {
294
+ rows = index_js_1.OINODbFactory.createRows(this.datamodel, body, params);
295
+ }
296
+ // OINOLog.debug("OINODbApi.doRequest - OINODataRow rows", {rows:rows})
297
+ }
212
298
  if (method == "GET") {
213
299
  await this._doGet(result, id, params);
214
300
  }
215
301
  else if (method == "PUT") {
216
- const rows = index_js_1.OINODbFactory.createRows(this.datamodel, body, params);
217
302
  if (!id) {
218
303
  result.setError(400, "HTTP PUT method requires an URL ID for the row that is updated!", "DoRequest");
219
304
  }
@@ -230,7 +315,6 @@ class OINODbApi {
230
315
  }
231
316
  }
232
317
  else if (method == "POST") {
233
- const rows = index_js_1.OINODbFactory.createRows(this.datamodel, body, params);
234
318
  if (id) {
235
319
  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");
236
320
  }
@@ -235,8 +235,11 @@ class OINONumberDataField extends OINODbDataField {
235
235
  *
236
236
  */
237
237
  deserializeCell(value) {
238
- if (!value) {
239
- return 0;
238
+ if (value === undefined) {
239
+ return undefined;
240
+ }
241
+ else if ((value == "") || (value == null)) {
242
+ return null;
240
243
  }
241
244
  else {
242
245
  return Number.parseFloat(value);
@@ -241,7 +241,7 @@ class OINODbDataModel {
241
241
  result += "\nLIMIT " + limit_sql;
242
242
  }
243
243
  result += ";";
244
- index_js_1.OINOLog.debug("OINODbDataModel.printSqlSelect", { result: result });
244
+ // OINOLog.debug("OINODbDataModel.printSqlSelect", {result:result})
245
245
  return result;
246
246
  }
247
247
  /**
@@ -6,6 +6,7 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.OINODbFactory = void 0;
9
+ const OINOResult_js_1 = require("../../types/src/OINOResult.js");
9
10
  const index_js_1 = require("./index.js");
10
11
  /**
11
12
  * Static factory class for easily creating things based on data
@@ -58,8 +59,21 @@ class OINODbFactory {
58
59
  * @param request HTTP Request
59
60
  */
60
61
  static createParamsFromRequest(request) {
61
- let result = { sqlParams: {} };
62
62
  const url = new URL(request.url);
63
+ let sql_params = {};
64
+ const filter = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_FILTER_PARAM);
65
+ if (filter) {
66
+ sql_params.filter = index_js_1.OINODbSqlFilter.parse(filter);
67
+ }
68
+ const order = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_ORDER_PARAM);
69
+ if (order) {
70
+ sql_params.order = index_js_1.OINODbSqlOrder.parse(order);
71
+ }
72
+ const limit = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_LIMIT_PARAM);
73
+ if (limit) {
74
+ sql_params.limit = index_js_1.OINODbSqlLimit.parse(limit);
75
+ }
76
+ let result = { sqlParams: sql_params };
63
77
  const content_type = request.headers.get("content-type");
64
78
  if (content_type == index_js_1.OINOContentType.csv) {
65
79
  result.requestType = index_js_1.OINOContentType.csv;
@@ -87,42 +101,17 @@ class OINODbFactory {
87
101
  if (result.responseType === undefined) {
88
102
  result.responseType = index_js_1.OINOContentType.json;
89
103
  }
90
- const filter = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_FILTER_PARAM);
91
- if (filter) {
92
- result.sqlParams.filter = index_js_1.OINODbSqlFilter.parse(filter);
104
+ const last_modified = request.headers.get("if-modified-since");
105
+ if (last_modified) {
106
+ result.lastModified = new Date(last_modified).getTime();
93
107
  }
94
- const order = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_ORDER_PARAM);
95
- if (order) {
96
- result.sqlParams.order = new index_js_1.OINODbSqlOrder(order);
97
- }
98
- const limit = url.searchParams.get(index_js_1.OINODbConfig.OINODB_SQL_LIMIT_PARAM);
99
- if (limit) {
100
- result.sqlParams.limit = new index_js_1.OINODbSqlLimit(limit);
108
+ const etags = request.headers.get("if-none-match")?.split(',').map(e => e.trim());
109
+ if (etags) {
110
+ result.etags = etags;
101
111
  }
102
112
  // OINOLog.debug("createParamsFromRequest", {params:result})
103
113
  return result;
104
114
  }
105
- /**
106
- * Creates a HTTP Response from API results.
107
- *
108
- * @param apiResult API results
109
- * @param requestParams API request parameters
110
- * @param responseHeaders Headers to include in the response
111
- *
112
- */
113
- static createResponseFromApiResult(apiResult, requestParams, responseHeaders = {}) {
114
- let response = null;
115
- if (apiResult.success && apiResult.data) {
116
- response = new Response(apiResult.data.writeString(requestParams.responseType), { status: apiResult.statusCode, statusText: apiResult.statusMessage, headers: responseHeaders });
117
- }
118
- else {
119
- response = new Response(JSON.stringify(apiResult), { status: apiResult.statusCode, statusText: apiResult.statusMessage, headers: responseHeaders });
120
- }
121
- for (let i = 0; i < apiResult.messages.length; i++) {
122
- response.headers.set('X-OINO-MESSAGE-' + i, apiResult.messages[i]);
123
- }
124
- return response;
125
- }
126
115
  /**
127
116
  * Creates HTML Response from API modelset.
128
117
  *
@@ -131,7 +120,7 @@ class OINODbFactory {
131
120
  *
132
121
  */
133
122
  static createHtmlFromData(modelset, template) {
134
- let result = "";
123
+ let html = "";
135
124
  const dataset = modelset.dataset;
136
125
  const datamodel = modelset.datamodel;
137
126
  while (!dataset.isEof()) {
@@ -151,44 +140,16 @@ class OINODbFactory {
151
140
  html_row = html_row.replaceAll('###' + f.name + '###', index_js_1.OINOStr.encode(value, index_js_1.OINOContentType.html));
152
141
  }
153
142
  html_row = html_row.replaceAll('###createHtmlFromData_temporary_oinoid###', index_js_1.OINOStr.encode(index_js_1.OINODbConfig.printOINOId(primary_key_values), index_js_1.OINOContentType.html));
154
- result += html_row + "\r\n";
143
+ html += html_row + "\r\n";
155
144
  dataset.next();
156
145
  }
157
- return result;
158
- }
159
- /**
160
- * Creates HTML Response from a row id.
161
- *
162
- * @param oinoId OINO id
163
- * @param template HTML template
164
- *
165
- */
166
- static createHtmlFromOinoId(oinoId, template) {
167
- let result = template.replaceAll('###' + index_js_1.OINODbConfig.OINODB_ID_FIELD + '###', index_js_1.OINOStr.encode(oinoId, index_js_1.OINOContentType.html));
168
- return result;
169
- }
170
- /**
171
- * Creates HTML Response from object properties.
172
- *
173
- * @param object object
174
- * @param template HTML template
175
- *
176
- */
177
- static createHtmlFromObject(object, template) {
178
- let result = template;
179
- for (let key in object) {
180
- const value = object[key];
181
- if (value) {
182
- result = result.replaceAll('###' + key + '###', index_js_1.OINOStr.encode(value.toString(), index_js_1.OINOContentType.html));
183
- }
184
- }
185
- result = result.replace(/###[^#]*###/g, "");
146
+ const result = new OINOResult_js_1.OINOHttpResult(html);
186
147
  return result;
187
148
  }
188
149
  /**
189
150
  * Creates HTML Response from API result.
190
151
  *
191
- * @param apiResult object
152
+ * @param result OINOResult-object
192
153
  * @param template HTML template
193
154
  * @param includeErrorMessages include debug messages in result
194
155
  * @param includeWarningMessages include debug messages in result
@@ -196,30 +157,31 @@ class OINODbFactory {
196
157
  * @param includeDebugMessages include debug messages in result
197
158
  *
198
159
  */
199
- static createHtmlFromApiResult(apiResult, template, includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
200
- let result = template;
201
- result = result.replaceAll('###statusCode###', index_js_1.OINOStr.encode(apiResult.statusCode.toString(), index_js_1.OINOContentType.html));
202
- result = result.replaceAll('###statusMessage###', index_js_1.OINOStr.encode(apiResult.statusMessage.toString(), index_js_1.OINOContentType.html));
160
+ static createHtmlNotificationFromResult(result, template, includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
161
+ let html = template;
162
+ html = html.replaceAll('###statusCode###', index_js_1.OINOStr.encode(result.statusCode.toString(), index_js_1.OINOContentType.html));
163
+ html = html.replaceAll('###statusMessage###', index_js_1.OINOStr.encode(result.statusMessage.toString(), index_js_1.OINOContentType.html));
203
164
  let messages = "";
204
- for (let i = 0; i < apiResult.messages.length; i++) {
205
- if (includeErrorMessages && apiResult.messages[i].startsWith(index_js_1.OINO_ERROR_PREFIX)) {
206
- messages += "<li>" + index_js_1.OINOStr.encode(apiResult.messages[i], index_js_1.OINOContentType.html) + "</li>";
165
+ for (let i = 0; i < result.messages.length; i++) {
166
+ if (includeErrorMessages && result.messages[i].startsWith(index_js_1.OINO_ERROR_PREFIX)) {
167
+ messages += "<li>" + index_js_1.OINOStr.encode(result.messages[i], index_js_1.OINOContentType.html) + "</li>";
207
168
  }
208
- if (includeWarningMessages && apiResult.messages[i].startsWith(index_js_1.OINO_WARNING_PREFIX)) {
209
- messages += "<li>" + index_js_1.OINOStr.encode(apiResult.messages[i], index_js_1.OINOContentType.html) + "</li>";
169
+ if (includeWarningMessages && result.messages[i].startsWith(index_js_1.OINO_WARNING_PREFIX)) {
170
+ messages += "<li>" + index_js_1.OINOStr.encode(result.messages[i], index_js_1.OINOContentType.html) + "</li>";
210
171
  }
211
- if (includeInfoMessages && apiResult.messages[i].startsWith(index_js_1.OINO_INFO_PREFIX)) {
212
- messages += "<li>" + index_js_1.OINOStr.encode(apiResult.messages[i], index_js_1.OINOContentType.html) + "</li>";
172
+ if (includeInfoMessages && result.messages[i].startsWith(index_js_1.OINO_INFO_PREFIX)) {
173
+ messages += "<li>" + index_js_1.OINOStr.encode(result.messages[i], index_js_1.OINOContentType.html) + "</li>";
213
174
  }
214
- if (includeDebugMessages && apiResult.messages[i].startsWith(index_js_1.OINO_DEBUG_PREFIX)) {
215
- messages += "<li>" + index_js_1.OINOStr.encode(apiResult.messages[i], index_js_1.OINOContentType.html) + "</li>";
175
+ if (includeDebugMessages && result.messages[i].startsWith(index_js_1.OINO_DEBUG_PREFIX)) {
176
+ messages += "<li>" + index_js_1.OINOStr.encode(result.messages[i], index_js_1.OINOContentType.html) + "</li>";
216
177
  }
217
178
  }
218
179
  if (messages) {
219
- result = result.replaceAll('###messages###', "<ul>" + messages + "</ul>");
180
+ html = html.replaceAll('###messages###', "<ul>" + messages + "</ul>");
220
181
  }
221
- result = result.replace(/###[^#]*###/g, "");
222
- return result;
182
+ html = html.replace(/###[^#]*###/g, "");
183
+ const http_result = new OINOResult_js_1.OINOHttpResult(html);
184
+ return http_result;
223
185
  }
224
186
  static _findCsvLineEnd(csvData, start) {
225
187
  const n = csvData.length;
@@ -496,7 +458,7 @@ class OINODbFactory {
496
458
  start = end;
497
459
  end = this._findMultipartBoundary(data, multipartBoundary, start);
498
460
  }
499
- index_js_1.OINOLog.debug("createRowFromFormdata: next row", { row: row });
461
+ // OINOLog.debug("createRowFromFormdata: next row", {row:row})
500
462
  result.push(row);
501
463
  return result;
502
464
  }
@@ -507,7 +469,7 @@ class OINODbFactory {
507
469
  const data_parts = data.trim().split('&');
508
470
  for (let i = 0; i < data_parts.length; i++) {
509
471
  const param_parts = data_parts[i].split('=');
510
- index_js_1.OINOLog.debug("createRowFromUrlencoded: next param", { param_parts: param_parts });
472
+ // OINOLog.debug("createRowFromUrlencoded: next param", {param_parts:param_parts})
511
473
  if (param_parts.length == 2) {
512
474
  const key = index_js_1.OINOStr.decodeUrlencode(param_parts[0]) || "";
513
475
  const field_index = datamodel.findFieldIndexByName(key);
@@ -525,7 +487,7 @@ class OINODbFactory {
525
487
  }
526
488
  // const value = requestParams[]
527
489
  }
528
- console.log("createRowFromUrlencoded: next row=" + row);
490
+ // console.log("createRowFromUrlencoded: next row=" + row)
529
491
  result.push(row);
530
492
  return result;
531
493
  }
@@ -559,5 +521,21 @@ class OINODbFactory {
559
521
  return [];
560
522
  }
561
523
  }
524
+ /**
525
+ * Create one data row from javascript object based on the datamodel.
526
+ * NOTE! Data assumed to be unserialized i.e. of the native type (string, number, boolean, Buffer)
527
+ *
528
+ * @param datamodel datamodel of the api
529
+ * @param data data as javascript object
530
+ *
531
+ */
532
+ static createRowFromObject(datamodel, data) {
533
+ const fields = datamodel.fields;
534
+ let result = new Array(fields.length);
535
+ for (let i = 0; i < fields.length; i++) {
536
+ result[i] = data[fields[i].name];
537
+ }
538
+ return result;
539
+ }
562
540
  }
563
541
  exports.OINODbFactory = OINODbFactory;
@@ -173,7 +173,7 @@ class OINODbSqlFilter {
173
173
  result += this._rightSide;
174
174
  }
175
175
  }
176
- index_js_1.OINOLog.debug("OINOFilter.toSql", { result: result });
176
+ // OINOLog.debug("OINOFilter.toSql", {result:result})
177
177
  return "(" + result + ")";
178
178
  }
179
179
  }
@@ -185,26 +185,47 @@ exports.OINODbSqlFilter = OINODbSqlFilter;
185
185
  class OINODbSqlOrder {
186
186
  static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC)?\s*?$/i;
187
187
  _columns;
188
- _directions;
188
+ _descending;
189
189
  /**
190
190
  * Constructor for `OINODbSqlOrder`.
191
191
  *
192
- * @param orderString string representation of filter from HTTP-request
192
+ * @param column_or_array single or array of columns to order on
193
+ * @param descending_or_array single or array of booleans if ordes is descending
193
194
  *
194
195
  */
195
- constructor(orderString) {
196
- // OINOLog.debug("OINODbSqlOrder.constructor", {orderString:orderString})
197
- this._columns = [];
198
- this._directions = [];
196
+ constructor(column_or_array, descending_or_array) {
197
+ index_js_1.OINOLog.debug("OINODbSqlOrder.constructor", { columns: column_or_array, directions: descending_or_array });
198
+ if (Array.isArray(column_or_array)) {
199
+ this._columns = column_or_array;
200
+ }
201
+ else {
202
+ this._columns = [column_or_array];
203
+ }
204
+ if (Array.isArray(descending_or_array)) {
205
+ this._descending = descending_or_array;
206
+ }
207
+ else {
208
+ this._descending = [descending_or_array];
209
+ }
210
+ }
211
+ /**
212
+ * Constructor for `OINODbSqlOrder` as parser of http parameter.
213
+ *
214
+ * @param orderString string representation of ordering from HTTP-request
215
+ *
216
+ */
217
+ static parse(orderString) {
218
+ let columns = [];
219
+ let directions = [];
199
220
  const column_strings = orderString.split(',');
200
221
  for (let i = 0; i < column_strings.length; i++) {
201
222
  let match = OINODbSqlOrder._orderColumnRegex.exec(column_strings[i]);
202
223
  if (match != null) {
203
- this._columns.push(match[1]);
204
- this._directions.push((match[2] || "ASC").toUpperCase());
224
+ columns.push(match[1]);
225
+ directions.push((match[2] || "DESC").toUpperCase() == "DESC");
205
226
  }
206
227
  }
207
- // OINOLog.debug("OINODbSqlOrder.constructor", {columns:this._columns, directions:this._directions})
228
+ return new OINODbSqlOrder(columns, directions);
208
229
  }
209
230
  /**
210
231
  * Does filter contain any valid conditions.
@@ -231,7 +252,13 @@ class OINODbSqlOrder {
231
252
  if (result) {
232
253
  result += ",";
233
254
  }
234
- result += dataModel.api.db.printSqlColumnname(field.name) + " " + this._directions[i];
255
+ result += dataModel.api.db.printSqlColumnname(field.name) + " ";
256
+ if (this._descending[i]) {
257
+ result += "DESC";
258
+ }
259
+ else {
260
+ result += "ASC";
261
+ }
235
262
  }
236
263
  }
237
264
  // OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
@@ -249,12 +276,25 @@ class OINODbSqlLimit {
249
276
  /**
250
277
  * Constructor for `OINODbSqlLimit`.
251
278
  *
252
- * @param limitString string representation of filter from HTTP-request
279
+ * @param limit maximum number of items to return
280
+ *
281
+ */
282
+ constructor(limit) {
283
+ this._limit = limit;
284
+ }
285
+ /**
286
+ * Constructor for `OINODbSqlLimit` as parser of http parameter.
287
+ *
288
+ * @param limitString string representation of limit from HTTP-request
253
289
  *
254
290
  */
255
- constructor(limitString) {
256
- this._limit = 0;
257
- this._limit = Number.parseInt(limitString);
291
+ static parse(limitString) {
292
+ try {
293
+ return new OINODbSqlLimit(Number.parseInt(limitString));
294
+ }
295
+ catch {
296
+ return new OINODbSqlLimit(-1);
297
+ }
258
298
  }
259
299
  /**
260
300
  * Does filter contain any valid conditions.
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OINODB_EMPTY_ROWS = exports.OINODB_EMPTY_ROW = exports.OINODbSwagger = exports.OINODbFactory = exports.OINODbConfig = exports.OINODbSqlBooleanOperation = exports.OINODbSqlLimit = exports.OINODbSqlComparison = exports.OINODbSqlOrder = exports.OINODbSqlFilter = exports.OINODbMemoryDataSet = exports.OINODbDataSet = exports.OINODb = exports.OINODatetimeDataField = exports.OINOBlobDataField = exports.OINOStringDataField = exports.OINONumberDataField = exports.OINOBooleanDataField = exports.OINODbDataField = exports.OINODbModelSet = exports.OINODbDataModel = exports.OINODbApi = exports.OINODbApiResult = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOBenchmark = exports.OINOStr = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINOContentType = void 0;
3
+ exports.OINODB_EMPTY_ROWS = exports.OINODB_EMPTY_ROW = exports.OINODbSwagger = exports.OINODbFactory = exports.OINODbConfig = exports.OINODbSqlBooleanOperation = exports.OINODbSqlLimit = exports.OINODbSqlComparison = exports.OINODbSqlOrder = exports.OINODbSqlFilter = exports.OINODbMemoryDataSet = exports.OINODbDataSet = exports.OINODb = exports.OINODatetimeDataField = exports.OINOBlobDataField = exports.OINOStringDataField = exports.OINONumberDataField = exports.OINOBooleanDataField = exports.OINODbDataField = exports.OINODbModelSet = exports.OINODbDataModel = exports.OINODbApi = exports.OINODbHtmlTemplate = exports.OINODbApiResult = exports.OINOHtmlTemplate = exports.OINOHttpResult = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOBenchmark = exports.OINOStr = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINOContentType = void 0;
4
4
  const types_1 = require("@oino-ts/types");
5
5
  Object.defineProperty(exports, "OINOContentType", { enumerable: true, get: function () { return types_1.OINOContentType; } });
6
6
  var types_2 = require("@oino-ts/types");
@@ -14,8 +14,11 @@ Object.defineProperty(exports, "OINOLog", { enumerable: true, get: function () {
14
14
  Object.defineProperty(exports, "OINOLogLevel", { enumerable: true, get: function () { return types_2.OINOLogLevel; } });
15
15
  Object.defineProperty(exports, "OINOConsoleLog", { enumerable: true, get: function () { return types_2.OINOConsoleLog; } });
16
16
  Object.defineProperty(exports, "OINOResult", { enumerable: true, get: function () { return types_2.OINOResult; } });
17
+ Object.defineProperty(exports, "OINOHttpResult", { enumerable: true, get: function () { return types_2.OINOHttpResult; } });
18
+ Object.defineProperty(exports, "OINOHtmlTemplate", { enumerable: true, get: function () { return types_2.OINOHtmlTemplate; } });
17
19
  var OINODbApi_js_1 = require("./OINODbApi.js");
18
20
  Object.defineProperty(exports, "OINODbApiResult", { enumerable: true, get: function () { return OINODbApi_js_1.OINODbApiResult; } });
21
+ Object.defineProperty(exports, "OINODbHtmlTemplate", { enumerable: true, get: function () { return OINODbApi_js_1.OINODbHtmlTemplate; } });
19
22
  Object.defineProperty(exports, "OINODbApi", { enumerable: true, get: function () { return OINODbApi_js_1.OINODbApi; } });
20
23
  var OINODbDataModel_js_1 = require("./OINODbDataModel.js");
21
24
  Object.defineProperty(exports, "OINODbDataModel", { enumerable: true, get: function () { return OINODbDataModel_js_1.OINODbDataModel; } });