@oino-ts/db 0.0.11

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 (48) hide show
  1. package/README.md +222 -0
  2. package/dist/cjs/OINODb.js +27 -0
  3. package/dist/cjs/OINODbApi.js +270 -0
  4. package/dist/cjs/OINODbConfig.js +86 -0
  5. package/dist/cjs/OINODbDataField.js +354 -0
  6. package/dist/cjs/OINODbDataModel.js +279 -0
  7. package/dist/cjs/OINODbDataSet.js +139 -0
  8. package/dist/cjs/OINODbFactory.js +563 -0
  9. package/dist/cjs/OINODbModelSet.js +267 -0
  10. package/dist/cjs/OINODbParams.js +280 -0
  11. package/dist/cjs/OINODbRequestParams.js +280 -0
  12. package/dist/cjs/OINODbSwagger.js +201 -0
  13. package/dist/cjs/index.js +51 -0
  14. package/dist/esm/OINODb.js +23 -0
  15. package/dist/esm/OINODbApi.js +265 -0
  16. package/dist/esm/OINODbConfig.js +82 -0
  17. package/dist/esm/OINODbDataField.js +345 -0
  18. package/dist/esm/OINODbDataModel.js +275 -0
  19. package/dist/esm/OINODbDataSet.js +134 -0
  20. package/dist/esm/OINODbFactory.js +559 -0
  21. package/dist/esm/OINODbModelSet.js +263 -0
  22. package/dist/esm/OINODbRequestParams.js +274 -0
  23. package/dist/esm/OINODbSwagger.js +197 -0
  24. package/dist/esm/index.js +17 -0
  25. package/dist/types/OINODb.d.ts +75 -0
  26. package/dist/types/OINODbApi.d.ts +57 -0
  27. package/dist/types/OINODbConfig.d.ts +52 -0
  28. package/dist/types/OINODbDataField.d.ts +202 -0
  29. package/dist/types/OINODbDataModel.d.ts +108 -0
  30. package/dist/types/OINODbDataSet.d.ts +95 -0
  31. package/dist/types/OINODbFactory.d.ts +99 -0
  32. package/dist/types/OINODbModelSet.d.ts +50 -0
  33. package/dist/types/OINODbRequestParams.d.ts +130 -0
  34. package/dist/types/OINODbSwagger.d.ts +25 -0
  35. package/dist/types/index.d.ts +103 -0
  36. package/package.json +35 -0
  37. package/src/OINODb.ts +98 -0
  38. package/src/OINODbApi.test.ts +243 -0
  39. package/src/OINODbApi.ts +270 -0
  40. package/src/OINODbConfig.ts +92 -0
  41. package/src/OINODbDataField.ts +372 -0
  42. package/src/OINODbDataModel.ts +290 -0
  43. package/src/OINODbDataSet.ts +170 -0
  44. package/src/OINODbFactory.ts +570 -0
  45. package/src/OINODbModelSet.ts +286 -0
  46. package/src/OINODbRequestParams.ts +281 -0
  47. package/src/OINODbSwagger.ts +209 -0
  48. package/src/index.ts +116 -0
@@ -0,0 +1,275 @@
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 { OINO_ERROR_PREFIX, OINODbConfig, OINONumberDataField, OINOLog } from "./index.js";
7
+ /**
8
+ * OINO Datamodel object for representing one database table and it's columns.
9
+ *
10
+ */
11
+ export class OINODbDataModel {
12
+ _columnLookup;
13
+ /** Database refererence of the table */
14
+ api;
15
+ /** Field refererences of the API */
16
+ fields;
17
+ /**
18
+ * Constructor of the data model.
19
+ * NOTE! OINODbDataModel.initialize must be called after constructor to populate fields.
20
+ *
21
+ * @param api api of the data model
22
+ *
23
+ */
24
+ constructor(api) {
25
+ this._columnLookup = {};
26
+ this.api = api;
27
+ this.fields = [];
28
+ // OINOLog_debug("OINODbDataModel (" + tableName + "):\n" + this._printTableDebug("\n"))
29
+ }
30
+ /**
31
+ * Initialize datamodel from SQL schema.
32
+ *
33
+ */
34
+ async initialize() {
35
+ await this.api.db.initializeApiDatamodel(this.api);
36
+ }
37
+ _printSqlColumnNames() {
38
+ let result = "";
39
+ for (let f of this.fields) {
40
+ if (result != "") {
41
+ result += ",";
42
+ }
43
+ result += f.printSqlColumnName();
44
+ }
45
+ return result;
46
+ }
47
+ _printSqlInsertColumnsAndValues(row) {
48
+ let columns = "";
49
+ let values = "";
50
+ for (let i = 0; i < this.fields.length; i++) {
51
+ const val = row[i];
52
+ // console.log("_printSqlInsertColumnsAndValues: row[" + i + "]=" + val)
53
+ if (val !== undefined) {
54
+ const f = this.fields[i];
55
+ if (values != "") {
56
+ columns += ",";
57
+ values += ",";
58
+ }
59
+ columns += f.printSqlColumnName();
60
+ values += f.printCellAsSqlValue(val);
61
+ }
62
+ }
63
+ // console.log("_printSqlInsertColumnsAndValues: columns=" + columns + ", values=" + values)
64
+ return "(" + columns + ") VALUES (" + values + ")";
65
+ }
66
+ _printSqlUpdateValues(row) {
67
+ let result = "";
68
+ for (let i = 0; i < this.fields.length; i++) {
69
+ const f = this.fields[i];
70
+ const val = row[i];
71
+ // OINOLog_debug("OINODbDataModel._printSqlUpdateValues", {field:f.name, primary_key:f.fieldParams.isPrimaryKey, val:val})
72
+ if ((!f.fieldParams.isPrimaryKey) && (val !== undefined)) {
73
+ if (result != "") {
74
+ result += ",";
75
+ }
76
+ result += f.printSqlColumnName() + "=" + f.printCellAsSqlValue(val);
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ _printSqlPrimaryKeyCondition(id_value) {
82
+ let result = "";
83
+ let i = 0;
84
+ const id_parts = id_value.split(OINODbConfig.OINODB_ID_SEPARATOR);
85
+ for (let f of this.fields) {
86
+ if (f.fieldParams.isPrimaryKey) {
87
+ if (result != "") {
88
+ result += " AND ";
89
+ }
90
+ let value = decodeURIComponent(id_parts[i]);
91
+ if ((f instanceof OINONumberDataField) && (this.api.hashid)) {
92
+ value = this.api.hashid.decode(value);
93
+ }
94
+ result += f.printSqlColumnName() + "=" + f.printCellAsSqlValue(value);
95
+ i = i + 1;
96
+ }
97
+ }
98
+ if (i != id_parts.length) {
99
+ throw new Error(OINO_ERROR_PREFIX + ": id '" + id_value + "' is not a valid key for table " + this.api.params.tableName);
100
+ }
101
+ return "(" + result + ")";
102
+ }
103
+ /**
104
+ * Add a field to the datamodel.
105
+ *
106
+ * @param field dataset field
107
+ *
108
+ */
109
+ addField(field) {
110
+ this.fields.push(field);
111
+ this._columnLookup[field.name] = this.fields.length - 1;
112
+ }
113
+ /**
114
+ * Find a field of a given name if any.
115
+ *
116
+ * @param name name of the field to find
117
+ *
118
+ */
119
+ findFieldByName(name) {
120
+ // OINOLog.debug("OINODbDataModel.findFieldByName", {_columnLookup:this._columnLookup})
121
+ const i = this._columnLookup[name];
122
+ if (i >= 0) {
123
+ return this.fields[i];
124
+ }
125
+ else {
126
+ return null;
127
+ }
128
+ }
129
+ /**
130
+ * Find index of a field of a given name if any.
131
+ *
132
+ * @param name name of the field to find
133
+ *
134
+ */
135
+ findFieldIndexByName(name) {
136
+ // OINOLog.debug("OINODbDataModel.findFieldIndexByName", {_columnLookup:this._columnLookup})
137
+ const i = this._columnLookup[name];
138
+ if (i >= 0) {
139
+ return i;
140
+ }
141
+ else {
142
+ return -1;
143
+ }
144
+ }
145
+ /**
146
+ * Find all fields based of given filter callback criteria (e.g. fields of certain data type, primary keys etc.)
147
+ *
148
+ * @param filter callback called for each field to include or not
149
+ *
150
+ */
151
+ filterFields(filter) {
152
+ let result = [];
153
+ for (let f of this.fields) {
154
+ if (filter(f)) {
155
+ result.push(f);
156
+ }
157
+ }
158
+ return result;
159
+ }
160
+ /**
161
+ * Return the primary key values of one row in order of the data model
162
+ *
163
+ * @param row data row
164
+ * @param hashidValues apply hashid when applicable
165
+ *
166
+ */
167
+ getRowPrimarykeyValues(row, hashidValues = false) {
168
+ let values = [];
169
+ for (let i = 0; i < this.fields.length; i++) {
170
+ const f = this.fields[i];
171
+ if (f.fieldParams.isPrimaryKey) {
172
+ const value = row[i]?.toString() || "";
173
+ if (hashidValues && value && (f instanceof OINONumberDataField) && this.api.hashid) {
174
+ values.push(this.api.hashid.encode(value));
175
+ }
176
+ else {
177
+ values.push(value);
178
+ }
179
+ }
180
+ }
181
+ return values;
182
+ }
183
+ /**
184
+ * Print debug information about the fields.
185
+ *
186
+ * @param separator string to separate field prints
187
+ *
188
+ */
189
+ printDebug(separator = "") {
190
+ let result = this.api.params.tableName + ":" + separator;
191
+ for (let f of this.fields) {
192
+ result += f.printColumnDebug() + separator;
193
+ }
194
+ return result;
195
+ }
196
+ /**
197
+ * Print all public properties (db, table name, fields) of the datamodel. Used
198
+ * in automated testing validate schema has stayed the same.
199
+ *
200
+ */
201
+ printFieldPublicPropertiesJson() {
202
+ const result = JSON.stringify(this.fields, (key, value) => {
203
+ if (key.startsWith("_")) {
204
+ return undefined;
205
+ }
206
+ else {
207
+ return value;
208
+ }
209
+ });
210
+ return result;
211
+ }
212
+ /**
213
+ * Print SQL select statement using optional id and filter.
214
+ *
215
+ * @param id OINO ID (i.e. combined primary key values)
216
+ * @param params OINO reqest params
217
+ *
218
+ */
219
+ printSqlSelect(id, params) {
220
+ let result = "SELECT " + this._printSqlColumnNames() + " FROM " + this.api.db.printSqlTablename(this.api.params.tableName);
221
+ const filter_sql = params.filter?.toSql(this) || "";
222
+ const order_sql = params.order?.toSql(this) || "";
223
+ const limit_sql = params.limit?.toSql(this) || "";
224
+ // OINOLog.debug("OINODbDataModel.printSqlSelect", {select_sql:result, filter_sql:filter_sql, order_sql:order_sql})
225
+ if ((id != "") && (filter_sql != "")) {
226
+ result += "\nWHERE " + this._printSqlPrimaryKeyCondition(id) + " AND " + filter_sql;
227
+ }
228
+ else if (id != "") {
229
+ result += "\nWHERE " + this._printSqlPrimaryKeyCondition(id);
230
+ }
231
+ else if (filter_sql != "") {
232
+ result += "\nWHERE " + filter_sql;
233
+ }
234
+ if (order_sql) {
235
+ result += "\nORDER BY " + order_sql;
236
+ }
237
+ if (limit_sql) {
238
+ result += "\nLIMIT " + limit_sql;
239
+ }
240
+ result += ";";
241
+ OINOLog.debug("OINODbDataModel.printSqlSelect", { result: result });
242
+ return result;
243
+ }
244
+ /**
245
+ * Print SQL insert statement from one data row.
246
+ *
247
+ * @param row one row of data in the data model
248
+ *
249
+ */
250
+ printSqlInsert(row) {
251
+ let result = "INSERT INTO " + this.api.db.printSqlTablename(this.api.params.tableName) + " " + this._printSqlInsertColumnsAndValues(row) + ";";
252
+ return result;
253
+ }
254
+ /**
255
+ * Print SQL insert statement from one data row.
256
+ *
257
+ * @param id OINO ID (i.e. combined primary key values)
258
+ * @param row one row of data in the data model
259
+ *
260
+ */
261
+ printSqlUpdate(id, row) {
262
+ let result = "UPDATE " + this.api.db.printSqlTablename(this.api.params.tableName) + " SET " + this._printSqlUpdateValues(row) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
263
+ return result;
264
+ }
265
+ /**
266
+ * Print SQL delete statement for id.
267
+ *
268
+ * @param id OINO ID (i.e. combined primary key values)
269
+ *
270
+ */
271
+ printSqlDelete(id) {
272
+ let result = "DELETE FROM " + this.api.db.printSqlTablename(this.api.params.tableName) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
273
+ return result;
274
+ }
275
+ }
@@ -0,0 +1,134 @@
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 { OINO_ERROR_PREFIX, OINODB_EMPTY_ROW } from "./index.js";
7
+ /**
8
+ * Base class for SQL results that can be asynchronously iterated (but
9
+ * not necessarity rewinded). Idea is to handle database specific mechanisms
10
+ * for returning and formatting conventions in the database specific
11
+ * implementation. Data might be in memory or streamed in chunks and
12
+ * `OINODbDataSet` will serve it out consistently.
13
+ *
14
+ */
15
+ export class OINODbDataSet {
16
+ _data;
17
+ /** Error messages */
18
+ messages;
19
+ /**
20
+ * Constructor for `OINODbDataSet`.
21
+ *
22
+ * @param data internal database specific data type (constructor will throw if invalid)
23
+ * @param messages error messages from SQL-query
24
+ *
25
+ */
26
+ constructor(data, messages = []) {
27
+ this._data = data;
28
+ this.messages = messages;
29
+ }
30
+ /**
31
+ * Checks if the messages contain errors.
32
+ *
33
+ */
34
+ hasErrors() {
35
+ for (let i = 0; i < this.messages.length; i++) {
36
+ if (this.messages[i].startsWith(OINO_ERROR_PREFIX)) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Checks if the messages contain errors.
44
+ *
45
+ */
46
+ getFirstError() {
47
+ for (let i = 0; i < this.messages.length; i++) {
48
+ if (this.messages[i].startsWith(OINO_ERROR_PREFIX)) {
49
+ return this.messages[i];
50
+ }
51
+ }
52
+ return "";
53
+ }
54
+ }
55
+ /**
56
+ * Generic in memory implementation of a data set where data is an array of rows. Used
57
+ * by BunSqlite and automated testing. Can be rewinded.
58
+ *
59
+ */
60
+ export class OINODbMemoryDataSet extends OINODbDataSet {
61
+ _rows;
62
+ _currentRow;
63
+ _eof;
64
+ /**
65
+ * Constructor of `OINODbMemoryDataSet`.
66
+ *
67
+ * @param data data as OINODataRow[] (constructor will throw if invalid)
68
+ * @param errors error messages from SQL-query
69
+ *
70
+ */
71
+ constructor(data, errors = []) {
72
+ super(data, errors);
73
+ if ((data == null) || !(Array.isArray(data))) {
74
+ throw new Error(OINO_ERROR_PREFIX + ": Data needs to be compatible with OINORow[]!"); // TODO: maybe check all rows
75
+ }
76
+ this._rows = data;
77
+ if (this.isEmpty()) {
78
+ this._currentRow = -1;
79
+ this._eof = true;
80
+ }
81
+ else {
82
+ this._currentRow = 0;
83
+ this._eof = false;
84
+ }
85
+ }
86
+ /**
87
+ * Is data set empty.
88
+ *
89
+ */
90
+ isEmpty() {
91
+ return (this._rows.length == 0);
92
+ }
93
+ /**
94
+ * Is there no more content, i.e. either dataset is empty or we have moved beyond last line
95
+ *
96
+ */
97
+ isEof() {
98
+ return (this._eof);
99
+ }
100
+ /**
101
+ * Moves dataset to the next row, returns !isEof().
102
+ *
103
+ */
104
+ next() {
105
+ if (this._currentRow < this._rows.length - 1) {
106
+ this._currentRow = this._currentRow + 1;
107
+ }
108
+ else {
109
+ this._eof = true;
110
+ }
111
+ return !this._eof;
112
+ }
113
+ /**
114
+ * Gets current row of data.
115
+ *
116
+ */
117
+ getRow() {
118
+ if ((this._currentRow >= 0) && (this._currentRow < this._rows.length)) {
119
+ return this._rows[this._currentRow];
120
+ }
121
+ else {
122
+ return OINODB_EMPTY_ROW;
123
+ }
124
+ }
125
+ /**
126
+ * Rewinds data set to the first row, returns !isEof().
127
+ *
128
+ */
129
+ first() {
130
+ this._currentRow = 0;
131
+ this._eof = this._rows.length == 0;
132
+ return !this._eof;
133
+ }
134
+ }