@oino-ts/common 0.16.2 → 0.17.1

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 ADDED
@@ -0,0 +1,183 @@
1
+ # OINO TS
2
+ OINO Is Not an ORM but it's trying to solve a similar problem for API development. Instead of mirroring your DB schema in code that needs manual updates, OINO will get the data schema from DBMS using SQL in real time. Every time your app starts, it has an updated data model which enables automatic (de)serialize SQL results to JSON/CSV and back. OINO works on the level below routing where you pass the method, URL ID, body and request parameters to the API-object. OINO will parse and validate the data against the data model and generate proper SQL for your DB. Because OINO knows how data is serialized (e.g. JSON), what column it belongs to (e.g. floating point number) and what the target database is, it knows how to parse, format and escape the value as valid SQL.
3
+
4
+ ```
5
+ const result:OINOApiResult = await api_orderdetails.doRequest("GET", id, body, params)
6
+ return new Response(result.modelset.writeString(OINOContentType.json))
7
+ ```
8
+
9
+
10
+ # GETTING STARTED
11
+
12
+ ### Setup
13
+ Install the `@oino-ts/db` npm package and necessary database packages and import them in your code.
14
+ ```
15
+ bun install @oino-ts/db
16
+ bun install @oino-ts/db-bunsqlite
17
+ ```
18
+
19
+ ```
20
+ import { OINODb, OINOApi, OINOFactory } from "@oino-ts/db";
21
+ import { OINODbBunSqlite } from "@oino-ts/db-bunsqlite"
22
+ ```
23
+
24
+ ### Register database and logger
25
+ Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/common_src.OINOConsoleLog.html) how to implement your own)
26
+
27
+ ```
28
+ OINOLog.setLogger(new OINOConsoleLog())
29
+ OINOFactory.registerDb("OINODbBunSqlite", OINODbBunSqlite)
30
+ ```
31
+
32
+ ### Create a database
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
+ ```
35
+ const db:OINODb = await OINOFactory.createDb( { type: "OINODbBunSqlite", url: "file://../localDb/northwind.sqlite" } )
36
+ ```
37
+
38
+ ### Create an API
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
+ ```
41
+ const api_employees:OINOApi = await OINOFactory.createApi(db, { tableName: "Employees", excludeFields:["BirthDate"] })
42
+ ```
43
+
44
+ ### Pass HTTP requests to API
45
+ When you receive a HTTP request, just pass the method, URL ID, body and params to the correct API, which will parse and validate input and return results.
46
+
47
+ ```
48
+ const result:OINOApiResult = await api_orderdetails.doRequest("GET", id, body, params)
49
+ ```
50
+
51
+ ### Write results back to HTTP Response
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
+ ```
54
+ return new Response(result.data.writeString(OINOContentType.json))
55
+ ```
56
+
57
+
58
+ # MAIN FEATURES
59
+
60
+ ## RESTfull
61
+ OINO maps HTTP methods GET/POST/PUT/DELETE to SQL operations SELECT/INSERT/UPDATE/DELETE. The GET/POST requests can be made without URL ID to get all rows or insert new ones and others target a single row using URL ID.
62
+
63
+ For example HTTP POST
64
+ ```
65
+ Request and response:
66
+ > curl.exe -X POST http://localhost:3001/orderdetails -H "Content-Type: application/json" --data '[{\"OrderID\":11077,\"ProductID\":99,\"UnitPrice\":19,\"Quantity\":1,\"Discount\":0}]'
67
+ {"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
68
+
69
+ SQL:
70
+ INSERT INTO [OrderDetails] ("OrderID","ProductID","UnitPrice","Quantity","Discount") VALUES (11077,99,19,1,0);
71
+ ```
72
+
73
+
74
+ ## Universal Serialization
75
+ OINO handles serialization of data to JSON/CSV/etc. and back based on the data model. It knows what columns exist, what is their data type and how to convert each to JSON/CSV and back. This allows also partial data to be sent, i.e. you can send only columns that need updating or even send extra columns and have them ignored.
76
+
77
+ - Files can be sent to BLOB fields using BASE64 or MIME multipart encoding. Also supports standard HTML form file submission to blob fields and returning them data url images.
78
+ - Datetimes are (optionally) normalized to ISO 8601 format.
79
+ - Extended JSON-encoding
80
+ - Unquoted literal `undefined` can be used to represent non-existent values (leaving property out works too but preserving structure might be easier e.g. when translating data).
81
+ - CSV
82
+ - Comma-separated, doublequotes.
83
+ - Unquoted literal `null` represents null values.
84
+ - Unquoted empty string represents undefined values.
85
+ - Form data
86
+ - Multipart-mixed and binary files not supported.
87
+ - Non-existent value line (i.e. nothing after the empty line) treated as a null value.
88
+ - Url-encoded
89
+ - No null values, missing properties treated as undefined.
90
+ - Multiple lines could be used to post multiple rows.
91
+
92
+
93
+ ## Database Abstraction
94
+ OINO functions as a database abstraction, providing a consistent interface for working with different databases. It abstracts out different conventions in connecting, making queries and formatting data.
95
+
96
+ Currently supported databases:
97
+ - Bun Sqlite through Bun native implementation
98
+ - Postgresql through [pg](https://www.npmjs.com/package/pg)-package
99
+ - Mariadb / Mysql-support through [mariadb](https://www.npmjs.com/package/mariadb)-package
100
+ - Sql Server through [mssql](https://www.npmjs.com/package/mssql)-package
101
+
102
+ ## Composite Keys
103
+ 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`.
104
+
105
+ ## Power Of SQL
106
+ Since OINO is just generating SQL, WHERE-conditions can be defined with [`OINODbSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html), order with [`OINODbSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.html), limits/paging with [`OINODbSqlLimit`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlLimit.html) and aggregation with [`OINODbSqlAggregate`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlAggregate.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.
107
+
108
+ ## Swagger Support
109
+ 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.
110
+ ```
111
+ if (url.pathname == "/swagger.json") {
112
+ return new Response(JSON.stringify(OINOSwagger.getApiDefinition(api_array)))
113
+ }
114
+ ```
115
+ ![Swagger definition with a data model schema](img/readme-swagger.png)
116
+
117
+ ## Node support
118
+ 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).
119
+
120
+ ## HTMX support
121
+ 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)).
122
+
123
+ ## Hashids
124
+ 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.
125
+
126
+
127
+ # STATUS
128
+ OINO is currently a side project which should considered in beta status. That means that semantic versioning is the goal but backwards incompatible changes can still happen in point releases if serious architectual issues are discovered.
129
+
130
+ ## Roadmap
131
+ Major features that are considered in future releases ()
132
+
133
+ ### Support for views
134
+ Simple cases of views probably work already in some databases but edge cases might get complicated.
135
+
136
+ For example issues could be
137
+ - How to handle a view which does not have a complete private key?
138
+ - What edge cases exist in updating views?
139
+ - Can views be handled transparently to the DBMS or is there some fundamentally platform specific behavior?
140
+
141
+ ### Batch updates
142
+ Supporting batch updates similar to batch inserts is slightly bending the RESTfull principles but would still be a useful optional feature in some situations.
143
+
144
+ ### Streaming
145
+ One core idea is to be efficient in not making unnecessary copies of the data and minimizing garbage collection debt. This can be taken further by implementing streaming, allowing large dataset to be written to HTTP response as SQL result rows are received.
146
+
147
+ ### SQL generation callbacks
148
+ It would be useful to allow developer to validate / override SQL generation to cover cases OINO does not support or even workaround issues.
149
+
150
+ ### Transactions
151
+ Even though the basic case for OINO is executing SQL operations on individual rows, having an option to use SQL transactions could make sense at least for batch operations.
152
+
153
+
154
+ # HELP
155
+
156
+ ## Bug reports
157
+ Fixing bugs is a priority and getting good quality bug reports helps. It's recommended to use the sample Northwind database included with project to replicate issues or make an SQL script export of the relevant table.
158
+
159
+ ## Feedback
160
+ Understanding and prioritizing the use cases for OINO is also important and feedback about how you'd use OINO is interesting. Feel free to raise issues and feature requests in Github, but understand that short term most of the effort goes towards reaching the beta stage.
161
+
162
+ ## Typescript / Javascript architecture
163
+ Typescript building with different targets and module-systemts and a ton of configuration is a complex domain and something I have little experience un so help in fixing problems and how thing ought to be done is appreciated.
164
+
165
+ # LINKS
166
+ - [Github repository](https://github.com/pragmatta/oino-ts)
167
+ - [NPM repository](https://www.npmjs.com/org/oino-ts)
168
+
169
+
170
+ # ACKNOWLEDGEMENTS
171
+
172
+ ## Libraries
173
+ OINO uses the following open source libraries and npm packages and I would like to thank everyone for their contributions:
174
+ - Postgresql [node-postgres package](https://www.npmjs.com/package/pg)
175
+ - Mariadb / Mysql [mariadb package](https://www.npmjs.com/package/mariadb)
176
+ - Sql Server [mssql package](https://www.npmjs.com/package/mssql)
177
+ - Custom base encoding [base-x package](https://www.npmjs.com/package/base-x)
178
+
179
+ ## Bun
180
+ OINO has been developed using the Bun runtime, not because of the speed improvements but for the first class Typescript support and integrated developper experience. Kudos on the bun team for making Typescript work more exiting again.
181
+
182
+ ## SQL Scripts
183
+ The SQL scripts for creating the sample Northwind database are based on [Google Code archive](https://code.google.com/archive/p/northwindextended/downloads) and have been further customized to ensure they would have identical data (in the scope of the automated testing).
@@ -66,7 +66,7 @@ class OINOHtmlTemplate {
66
66
  }
67
67
  }
68
68
  _createHttpResult(html) {
69
- const result = new _1.OINOHttpResult(html);
69
+ const result = new _1.OINOHttpResult({ body: html });
70
70
  if (this.expires >= 1) {
71
71
  result.expires = Math.round(this.expires);
72
72
  }
@@ -183,8 +183,8 @@ class OINOHtmlTemplate {
183
183
  */
184
184
  renderFromResult(result, messageSeparator = "", includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
185
185
  _1.OINOBenchmark.startMetric("OINOHtmlTemplate", "renderFromResult");
186
- this.setVariableFromValue("statusCode", result.statusCode.toString());
187
- this.setVariableFromValue("statusMessage", result.statusMessage.toString());
186
+ this.setVariableFromValue("status", result.status.toString());
187
+ this.setVariableFromValue("statusText", result.statusText.toString());
188
188
  let messages = [];
189
189
  for (let i = 0; i < result.messages.length; i++) {
190
190
  if (includeErrorMessages && result.messages[i].startsWith(_1.OINO_ERROR_PREFIX)) {
@@ -0,0 +1,114 @@
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.OINOHttpRequest = exports.OINORequest = void 0;
9
+ const _1 = require(".");
10
+ const index_js_1 = require("./index.js");
11
+ /**
12
+ * OINO API request result object with returned data and/or http status code/message and
13
+ * error / warning messages.
14
+ *
15
+ */
16
+ class OINORequest {
17
+ /** Key-value parameters */
18
+ params;
19
+ /**
20
+ * Constructor of OINORequest.
21
+ *
22
+ * @param init initialization values
23
+ *
24
+ */
25
+ constructor(init) {
26
+ this.params = init?.params ?? {};
27
+ }
28
+ /**
29
+ * Copy values from different result.
30
+ *
31
+ * @param request source value
32
+ */
33
+ copy(request) {
34
+ this.params = { ...request.params };
35
+ }
36
+ }
37
+ exports.OINORequest = OINORequest;
38
+ /**
39
+ * Specialized result for HTTP responses.
40
+ */
41
+ class OINOHttpRequest extends OINORequest {
42
+ url;
43
+ method;
44
+ headers;
45
+ body;
46
+ requestType;
47
+ responseType;
48
+ multipartBoundary;
49
+ lastModified;
50
+ etags;
51
+ /**
52
+ * Constructor for a `OINOHttpRequest`
53
+ *
54
+ * @param init initialization values
55
+ *
56
+ */
57
+ constructor(init) {
58
+ super(init);
59
+ this.url = init.url;
60
+ this.method = init.method ?? "GET";
61
+ this.headers = init.headers ?? {};
62
+ this.body = init.body ?? "";
63
+ this.multipartBoundary = "";
64
+ this.lastModified = init.lastModified;
65
+ if (init.multipartBoundary) {
66
+ this.multipartBoundary = init.multipartBoundary;
67
+ }
68
+ if (init.requestType) {
69
+ this.requestType = init.requestType;
70
+ }
71
+ else {
72
+ const request_type_param = this.url?.searchParams.get(index_js_1.OINO_REQUEST_TYPE_PARAM) || this.headers["content-type"]; // content-type header can be overridden by query parameter
73
+ if (request_type_param == _1.OINOContentType.csv) {
74
+ this.requestType = _1.OINOContentType.csv;
75
+ }
76
+ else if (request_type_param == _1.OINOContentType.urlencode) {
77
+ this.requestType = _1.OINOContentType.urlencode;
78
+ }
79
+ else if (request_type_param?.startsWith(_1.OINOContentType.formdata)) {
80
+ this.requestType = _1.OINOContentType.formdata;
81
+ if (!this.multipartBoundary) {
82
+ this.multipartBoundary = request_type_param.split('boundary=')[1] || "";
83
+ }
84
+ }
85
+ else {
86
+ this.requestType = _1.OINOContentType.json;
87
+ }
88
+ }
89
+ if (init.responseType) {
90
+ this.responseType = init.responseType;
91
+ }
92
+ else {
93
+ const response_type_param = this.url?.searchParams.get(index_js_1.OINO_RESPONSE_TYPE_PARAM) || this.headers["accept"]; // accept header can be overridden by query parameter
94
+ const accept_types = response_type_param?.split(', ') || [];
95
+ let response_type = undefined;
96
+ for (let i = 0; i < accept_types.length; i++) {
97
+ if (Object.values(_1.OINOContentType).includes(accept_types[i])) {
98
+ response_type = accept_types[i];
99
+ break;
100
+ }
101
+ }
102
+ this.responseType = response_type ?? _1.OINOContentType.json;
103
+ }
104
+ const last_modified = this.headers["if-modified-since"];
105
+ if (last_modified) {
106
+ this.lastModified = new Date(last_modified).getTime();
107
+ }
108
+ const etags = this.headers["if-none-match"]?.split(',').map(e => e.trim());
109
+ if (etags) {
110
+ this.etags = etags;
111
+ }
112
+ }
113
+ }
114
+ exports.OINOHttpRequest = OINOHttpRequest;
@@ -17,20 +17,22 @@ class OINOResult {
17
17
  /** Wheter request was successfully executed */
18
18
  success;
19
19
  /** HTTP status code */
20
- statusCode;
20
+ status;
21
21
  /** HTTP status message */
22
- statusMessage;
22
+ statusText;
23
23
  /** Error / warning messages */
24
24
  messages;
25
25
  /**
26
26
  * Constructor of OINOResult.
27
27
  *
28
+ * @param init initialization values
29
+ *
28
30
  */
29
- constructor() {
30
- this.success = true;
31
- this.statusCode = 200;
32
- this.statusMessage = "OK";
33
- this.messages = [];
31
+ constructor(init) {
32
+ this.success = init?.success ?? true;
33
+ this.status = init?.status ?? 200;
34
+ this.statusText = init?.statusText ?? "OK";
35
+ this.messages = init?.messages ?? [];
34
36
  }
35
37
  /**
36
38
  * Copy values from different result.
@@ -39,8 +41,8 @@ class OINOResult {
39
41
  */
40
42
  copy(result) {
41
43
  this.success = result.success;
42
- this.statusCode = result.statusCode;
43
- this.statusMessage = result.statusMessage;
44
+ this.status = result.status;
45
+ this.statusText = result.statusText;
44
46
  this.messages = result.messages.slice();
45
47
  }
46
48
  /**
@@ -49,28 +51,28 @@ class OINOResult {
49
51
  */
50
52
  setOk() {
51
53
  this.success = true;
52
- this.statusCode = 200;
53
- this.statusMessage = "OK";
54
+ this.status = 200;
55
+ this.statusText = "OK";
54
56
  }
55
57
  /**
56
58
  * Set HTTP error status using given code and message. Returns self reference for chaining.
57
59
  *
58
- * @param statusCode HTTP status code
59
- * @param statusMessage HTTP status message
60
+ * @param status HTTP status code
61
+ * @param statusText HTTP status message
60
62
  * @param operation operation where error occured
61
63
  *
62
64
  */
63
- setError(statusCode, statusMessage, operation) {
65
+ setError(status, statusText, operation) {
64
66
  this.success = false;
65
- this.statusCode = statusCode;
66
- if (this.statusMessage != "OK") {
67
- this.messages.push(this.statusMessage); // latest error becomes status, but if there was something non-trivial, add it to the messages
67
+ this.status = status;
68
+ if (this.statusText != "OK") {
69
+ this.messages.push(this.statusText); // latest error becomes status, but if there was something non-trivial, add it to the messages
68
70
  }
69
- if (statusMessage.startsWith(_1.OINO_ERROR_PREFIX)) {
70
- this.statusMessage = statusMessage;
71
+ if (statusText.startsWith(_1.OINO_ERROR_PREFIX)) {
72
+ this.statusText = statusText;
71
73
  }
72
74
  else {
73
- this.statusMessage = _1.OINO_ERROR_PREFIX + " (" + operation + "): " + statusMessage;
75
+ this.statusText = _1.OINO_ERROR_PREFIX + " (" + operation + "): " + statusText;
74
76
  }
75
77
  return this;
76
78
  }
@@ -153,17 +155,7 @@ class OINOResult {
153
155
  *
154
156
  */
155
157
  printLog() {
156
- return "OINOResult: statusCode=" + this.statusCode + ", statusMessage=" + this.statusMessage + ", messages=[" + this.messages.join(", ") + "]";
157
- }
158
- /**
159
- * Get a Response object from the result values.
160
- *
161
- * @param headers HTTP headers (overrides existing values)
162
- */
163
- getStatusResponse(headers) {
164
- const result = new Response(this.statusMessage, { status: this.statusCode, headers: headers });
165
- result.headers.set('Content-Length', this.statusMessage.length.toString());
166
- return result;
158
+ return "OINOResult: status=" + this.status + ", statusText=" + this.statusText + ", messages=[" + this.messages.join(", ") + "]";
167
159
  }
168
160
  }
169
161
  exports.OINOResult = OINOResult;
@@ -174,21 +166,23 @@ class OINOHttpResult extends OINOResult {
174
166
  _etag;
175
167
  /** HTTP body data */
176
168
  body;
177
- /** HTTP cache expiration value */
169
+ /** HTTP cache expiration value
170
+ * Note: default 0 means no expiration and 'Pragma: no-cache' is set.
171
+ */
178
172
  expires;
179
173
  /** HTTP cache last-modified value */
180
174
  lastModified;
181
175
  /**
182
176
  * Constructor for a `OINOHttpResult`
183
177
  *
184
- * @param body HTTP body
178
+ * @param init initialization values
185
179
  *
186
180
  */
187
- constructor(body) {
188
- super();
189
- this.body = body;
190
- this.expires = 0;
191
- this.lastModified = 0;
181
+ constructor(init) {
182
+ super(init);
183
+ this.body = init?.body ?? "";
184
+ this.expires = init?.expires ?? 0;
185
+ this.lastModified = init?.lastModified ?? 0;
192
186
  this._etag = "";
193
187
  }
194
188
  /**
@@ -208,7 +202,7 @@ class OINOHttpResult extends OINOResult {
208
202
  * @param headers HTTP headers (overrides existing values)
209
203
  */
210
204
  getHttpResponse(headers) {
211
- const result = new Response(this.body, { status: this.statusCode, statusText: this.statusMessage, headers: headers });
205
+ const result = new Response(this.body, { status: this.status, statusText: this.statusText, headers: headers });
212
206
  result.headers.set('Content-Length', this.body.length.toString());
213
207
  if (this.lastModified > 0) {
214
208
  result.headers.set('Last-Modified', new Date(this.lastModified).toUTCString());
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OINOContentType = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINO_EMPTY_FORMATTER = exports.OINOFormatter = exports.OINOHtmlTemplate = exports.OINOStr = exports.OINOHttpResult = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOMemoryBenchmark = exports.OINOBenchmark = void 0;
3
+ exports.OINOContentType = exports.OINO_RESPONSE_TYPE_PARAM = exports.OINO_REQUEST_TYPE_PARAM = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINO_EMPTY_FORMATTER = exports.OINOFormatter = exports.OINOHtmlTemplate = exports.OINOStr = exports.OINOHttpRequest = exports.OINORequest = exports.OINOHttpResult = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOMemoryBenchmark = exports.OINOBenchmark = void 0;
4
4
  var OINOBenchmark_js_1 = require("./OINOBenchmark.js");
5
5
  Object.defineProperty(exports, "OINOBenchmark", { enumerable: true, get: function () { return OINOBenchmark_js_1.OINOBenchmark; } });
6
6
  Object.defineProperty(exports, "OINOMemoryBenchmark", { enumerable: true, get: function () { return OINOBenchmark_js_1.OINOMemoryBenchmark; } });
@@ -11,6 +11,9 @@ Object.defineProperty(exports, "OINOConsoleLog", { enumerable: true, get: functi
11
11
  var OINOResult_js_1 = require("./OINOResult.js");
12
12
  Object.defineProperty(exports, "OINOResult", { enumerable: true, get: function () { return OINOResult_js_1.OINOResult; } });
13
13
  Object.defineProperty(exports, "OINOHttpResult", { enumerable: true, get: function () { return OINOResult_js_1.OINOHttpResult; } });
14
+ var OINORequest_js_1 = require("./OINORequest.js");
15
+ Object.defineProperty(exports, "OINORequest", { enumerable: true, get: function () { return OINORequest_js_1.OINORequest; } });
16
+ Object.defineProperty(exports, "OINOHttpRequest", { enumerable: true, get: function () { return OINORequest_js_1.OINOHttpRequest; } });
14
17
  var OINOStr_js_1 = require("./OINOStr.js");
15
18
  Object.defineProperty(exports, "OINOStr", { enumerable: true, get: function () { return OINOStr_js_1.OINOStr; } });
16
19
  var OINOHtmlTemplate_js_1 = require("./OINOHtmlTemplate.js");
@@ -26,6 +29,10 @@ exports.OINO_WARNING_PREFIX = "OINO WARNING";
26
29
  exports.OINO_INFO_PREFIX = "OINO INFO";
27
30
  /** OINO debug message prefix */
28
31
  exports.OINO_DEBUG_PREFIX = "OINO DEBUG";
32
+ /** Name of the OINOContentType-parameter request */
33
+ exports.OINO_REQUEST_TYPE_PARAM = "oinorequesttype";
34
+ /** Name of the OINOContentType-parameter request */
35
+ exports.OINO_RESPONSE_TYPE_PARAM = "oinoresponsetype";
29
36
  /**
30
37
  * Supported content format mime-types
31
38
  */
@@ -63,7 +63,7 @@ export class OINOHtmlTemplate {
63
63
  }
64
64
  }
65
65
  _createHttpResult(html) {
66
- const result = new OINOHttpResult(html);
66
+ const result = new OINOHttpResult({ body: html });
67
67
  if (this.expires >= 1) {
68
68
  result.expires = Math.round(this.expires);
69
69
  }
@@ -180,8 +180,8 @@ export class OINOHtmlTemplate {
180
180
  */
181
181
  renderFromResult(result, messageSeparator = "", includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
182
182
  OINOBenchmark.startMetric("OINOHtmlTemplate", "renderFromResult");
183
- this.setVariableFromValue("statusCode", result.statusCode.toString());
184
- this.setVariableFromValue("statusMessage", result.statusMessage.toString());
183
+ this.setVariableFromValue("status", result.status.toString());
184
+ this.setVariableFromValue("statusText", result.statusText.toString());
185
185
  let messages = [];
186
186
  for (let i = 0; i < result.messages.length; i++) {
187
187
  if (includeErrorMessages && result.messages[i].startsWith(OINO_ERROR_PREFIX)) {
@@ -0,0 +1,109 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+ import { OINOContentType } from ".";
7
+ import { OINO_REQUEST_TYPE_PARAM, OINO_RESPONSE_TYPE_PARAM } from "./index.js";
8
+ /**
9
+ * OINO API request result object with returned data and/or http status code/message and
10
+ * error / warning messages.
11
+ *
12
+ */
13
+ export class OINORequest {
14
+ /** Key-value parameters */
15
+ params;
16
+ /**
17
+ * Constructor of OINORequest.
18
+ *
19
+ * @param init initialization values
20
+ *
21
+ */
22
+ constructor(init) {
23
+ this.params = init?.params ?? {};
24
+ }
25
+ /**
26
+ * Copy values from different result.
27
+ *
28
+ * @param request source value
29
+ */
30
+ copy(request) {
31
+ this.params = { ...request.params };
32
+ }
33
+ }
34
+ /**
35
+ * Specialized result for HTTP responses.
36
+ */
37
+ export class OINOHttpRequest extends OINORequest {
38
+ url;
39
+ method;
40
+ headers;
41
+ body;
42
+ requestType;
43
+ responseType;
44
+ multipartBoundary;
45
+ lastModified;
46
+ etags;
47
+ /**
48
+ * Constructor for a `OINOHttpRequest`
49
+ *
50
+ * @param init initialization values
51
+ *
52
+ */
53
+ constructor(init) {
54
+ super(init);
55
+ this.url = init.url;
56
+ this.method = init.method ?? "GET";
57
+ this.headers = init.headers ?? {};
58
+ this.body = init.body ?? "";
59
+ this.multipartBoundary = "";
60
+ this.lastModified = init.lastModified;
61
+ if (init.multipartBoundary) {
62
+ this.multipartBoundary = init.multipartBoundary;
63
+ }
64
+ if (init.requestType) {
65
+ this.requestType = init.requestType;
66
+ }
67
+ else {
68
+ const request_type_param = this.url?.searchParams.get(OINO_REQUEST_TYPE_PARAM) || this.headers["content-type"]; // content-type header can be overridden by query parameter
69
+ if (request_type_param == OINOContentType.csv) {
70
+ this.requestType = OINOContentType.csv;
71
+ }
72
+ else if (request_type_param == OINOContentType.urlencode) {
73
+ this.requestType = OINOContentType.urlencode;
74
+ }
75
+ else if (request_type_param?.startsWith(OINOContentType.formdata)) {
76
+ this.requestType = OINOContentType.formdata;
77
+ if (!this.multipartBoundary) {
78
+ this.multipartBoundary = request_type_param.split('boundary=')[1] || "";
79
+ }
80
+ }
81
+ else {
82
+ this.requestType = OINOContentType.json;
83
+ }
84
+ }
85
+ if (init.responseType) {
86
+ this.responseType = init.responseType;
87
+ }
88
+ else {
89
+ const response_type_param = this.url?.searchParams.get(OINO_RESPONSE_TYPE_PARAM) || this.headers["accept"]; // accept header can be overridden by query parameter
90
+ const accept_types = response_type_param?.split(', ') || [];
91
+ let response_type = undefined;
92
+ for (let i = 0; i < accept_types.length; i++) {
93
+ if (Object.values(OINOContentType).includes(accept_types[i])) {
94
+ response_type = accept_types[i];
95
+ break;
96
+ }
97
+ }
98
+ this.responseType = response_type ?? OINOContentType.json;
99
+ }
100
+ const last_modified = this.headers["if-modified-since"];
101
+ if (last_modified) {
102
+ this.lastModified = new Date(last_modified).getTime();
103
+ }
104
+ const etags = this.headers["if-none-match"]?.split(',').map(e => e.trim());
105
+ if (etags) {
106
+ this.etags = etags;
107
+ }
108
+ }
109
+ }