@oino-ts/db 0.21.2 → 1.0.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.
Files changed (39) hide show
  1. package/dist/cjs/OINODb.js +6 -144
  2. package/dist/cjs/OINODbApi.js +50 -318
  3. package/dist/cjs/OINODbConfig.js +10 -10
  4. package/dist/cjs/OINODbConstants.js +10 -0
  5. package/dist/cjs/OINODbDataField.js +28 -70
  6. package/dist/cjs/OINODbDataModel.js +30 -144
  7. package/dist/cjs/OINODbFactory.js +2 -2
  8. package/dist/cjs/OINODbModelSet.js +23 -23
  9. package/dist/cjs/OINODbQueryParams.js +201 -0
  10. package/dist/cjs/index.js +12 -41
  11. package/dist/esm/OINODb.js +6 -142
  12. package/dist/esm/OINODbApi.js +49 -314
  13. package/dist/esm/OINODbConstants.js +7 -0
  14. package/dist/esm/OINODbDataModel.js +31 -145
  15. package/dist/esm/OINODbFactory.js +1 -1
  16. package/dist/esm/OINODbQueryParams.js +194 -0
  17. package/dist/esm/index.js +4 -14
  18. package/dist/types/OINODb.d.ts +6 -173
  19. package/dist/types/OINODbApi.d.ts +18 -104
  20. package/dist/types/OINODbConstants.d.ts +23 -0
  21. package/dist/types/OINODbDataModel.d.ts +7 -61
  22. package/dist/types/OINODbFactory.d.ts +5 -2
  23. package/dist/types/OINODbQueryParams.d.ts +72 -0
  24. package/dist/types/index.d.ts +4 -108
  25. package/package.json +37 -37
  26. package/src/OINODb.ts +99 -348
  27. package/src/OINODbApi.test.ts +507 -498
  28. package/src/OINODbApi.ts +389 -667
  29. package/src/OINODbConstants.ts +32 -0
  30. package/src/OINODbDataModel.ts +191 -307
  31. package/src/OINODbFactory.ts +73 -68
  32. package/src/OINODbQueryParams.ts +203 -0
  33. package/src/index.ts +6 -118
  34. package/src/OINODbConfig.ts +0 -98
  35. package/src/OINODbDataField.ts +0 -405
  36. package/src/OINODbModelSet.ts +0 -353
  37. package/src/OINODbParser.ts +0 -438
  38. package/src/OINODbSqlParams.ts +0 -593
  39. package/src/OINODbSwagger.ts +0 -209
@@ -0,0 +1,32 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import { OINODb } from "./OINODb.js"
8
+
9
+ /**
10
+ * Database class (constructor) type
11
+ * @param dbParams database parameters
12
+ */
13
+ export type OINODbConstructor = new (dbParams:OINODbParams) => OINODb
14
+
15
+ /** Database parameters */
16
+ export type OINODbParams = {
17
+ /** Name of the database class (e.g. OINODbPostgresql) */
18
+ type: string
19
+ /** Connection URL, either file://-path or an IP-address or an HTTP-url */
20
+ url: string
21
+ /** Name of the database */
22
+ database: string
23
+ /** TCP port of the database */
24
+ port?: number
25
+ /** Username used to authenticate */
26
+ user?: string
27
+ /** Password used to authenticate */
28
+ password?: string
29
+ }
30
+
31
+ /** Constant for undefined values */
32
+ export const OINODB_UNDEFINED = "" // original idea was to have a defined literal that get's swapped back to undefined, but current implementation just leaves it out at serialization (so value does not matter)
@@ -1,307 +1,191 @@
1
- /*
2
- * This Source Code Form is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
- */
6
-
7
- import { OINO_ERROR_PREFIX } from "@oino-ts/common"
8
- import { OINODbDataField, OINODbApi, OINODataRow, OINODbDataFieldFilter, OINODbConfig, OINODbSqlParams, OINONumberDataField, OINODbSqlSelect, OINODB_UNDEFINED } from "./index.js";
9
-
10
- /**
11
- * OINO Datamodel object for representing one database table and it's columns.
12
- *
13
- */
14
- export class OINODbDataModel {
15
- private _fieldIndexLookup:Record<string, number>;
16
-
17
- /** Database refererence of the table */
18
- readonly api:OINODbApi
19
-
20
- /** Field refererences of the API */
21
- readonly fields: OINODbDataField[]
22
-
23
- /**
24
- * Constructor of the data model.
25
- * NOTE! OINODbDataModel.initialize must be called after constructor to populate fields.
26
- *
27
- * @param api api of the data model
28
- *
29
- */
30
- constructor(api:OINODbApi) {
31
- this._fieldIndexLookup = {}
32
- this.api = api
33
- this.fields = []
34
- }
35
- /**
36
- * Initialize datamodel from SQL schema.
37
- *
38
- */
39
- async initialize() {
40
- await this.api.db.initializeApiDatamodel(this.api)
41
- }
42
-
43
- private _printSqlColumnNames(select?:OINODbSqlSelect): string {
44
- let result: string = "";
45
- for (let i=0; i < this.fields.length; i++) {
46
- const f:OINODbDataField = this.fields[i]
47
- if (select?.isSelected(f) === false) { // if a field is not selected, we include a constant and correct fieldname instead so that dimensions of the data don't change but no unnecessary data is fetched
48
- result += f.db.printSqlString(OINODB_UNDEFINED) + " as " + f.printSqlColumnName()+","
49
- } else {
50
- result += f.printSqlColumnName()+","
51
- }
52
- }
53
- return result.substring(0, result.length-1)
54
- }
55
-
56
- private _printSqlInsertColumnsAndValues(row: OINODataRow): [string, string] {
57
- let columns: string = "";
58
- let values: string = "";
59
- for (let i=0; i< this.fields.length; i++) {
60
- const val = row[i];
61
- // console.log("_printSqlInsertColumnsAndValues: row[" + i + "]=" + val)
62
- if (val !== undefined) {
63
- const f = this.fields[i]
64
- if (values != "") {
65
- columns += ",";
66
- values += ",";
67
- }
68
- columns += f.printSqlColumnName();
69
- values += f.printCellAsSqlValue(val);
70
- }
71
- }
72
- // console.log("_printSqlInsertColumnsAndValues: columns=" + columns + ", values=" + values)
73
- return [ columns, values ]
74
- }
75
-
76
- private _printSqlUpdateValues(row: OINODataRow): string {
77
- let result: string = "";
78
- for (let i=0; i< this.fields.length; i++) {
79
- const f = this.fields[i]
80
- const val = row[i];
81
- if ((!f.fieldParams.isPrimaryKey) && (val !== undefined)) {
82
- if (result != "") {
83
- result += ",";
84
- }
85
- result += f.printSqlColumnName() + "=" + f.printCellAsSqlValue(val);
86
- }
87
- }
88
- if (result == "") {
89
- throw new Error(OINO_ERROR_PREFIX + ": no valid updatable fields provided for row!")
90
- }
91
- return result;
92
- }
93
-
94
- private _printSqlPrimaryKeyCondition(id_value: string): string {
95
- let result: string = ""
96
- let i:number = 0
97
- const id_parts = id_value.split(OINODbConfig.OINODB_ID_SEPARATOR)
98
- for (let f of this.fields) {
99
- if (f.fieldParams.isPrimaryKey) {
100
- if (result != "") {
101
- result += " AND "
102
- }
103
- let value = decodeURIComponent(id_parts[i])
104
- if ((f instanceof OINONumberDataField) && (this.api.hashid)) {
105
- value = this.api.hashid.decode(value)
106
- }
107
- value = f.printCellAsSqlValue(value)
108
- if (value == "") { // ids are user input and could be specially crafted to be empty
109
- throw new Error(OINO_ERROR_PREFIX + ": empty condition for id '" + id_value + "' for table " + this.api.params.tableName)
110
- }
111
- result += f.printSqlColumnName() + "=" + value;
112
- i = i + 1
113
- }
114
- }
115
- if (i != id_parts.length) {
116
- throw new Error(OINO_ERROR_PREFIX + ": id '" + id_value + "' is not a valid key for table " + this.api.params.tableName)
117
- }
118
- return "(" + result + ")";
119
- }
120
-
121
- private _printSqlPrimaryKeyColumns(): string[] {
122
- let result: string[] = []
123
- for (let f of this.fields) {
124
- if (f.fieldParams.isPrimaryKey) {
125
- result.push(this.api.db.printSqlColumnname(f.name))
126
- }
127
- }
128
- return result
129
- }
130
-
131
- /**
132
- * Add a field to the datamodel.
133
- *
134
- * @param field dataset field
135
- *
136
- */
137
- addField(field:OINODbDataField) {
138
- this.fields.push(field)
139
- this._fieldIndexLookup[field.name] = this.fields.length-1
140
- }
141
-
142
- /**
143
- * Find a field of a given name if any.
144
- *
145
- * @param name name of the field to find
146
- *
147
- */
148
- findFieldByName(name:string):OINODbDataField|null {
149
- const i:number = this._fieldIndexLookup[name]
150
- if (i >= 0) {
151
- return this.fields[i]
152
- } else {
153
- return null
154
- }
155
- }
156
-
157
- /**
158
- * Find index of a field of a given name if any.
159
- *
160
- * @param name name of the field to find
161
- *
162
- */
163
- findFieldIndexByName(name:string):number {
164
- const i:number = this._fieldIndexLookup[name]
165
- if (i >= 0) {
166
- return i
167
- } else {
168
- return -1
169
- }
170
- }
171
-
172
- /**
173
- * Find all fields based of given filter callback criteria (e.g. fields of certain data type, primary keys etc.)
174
- *
175
- * @param filter callback called for each field to include or not
176
- *
177
- */
178
- filterFields(filter:OINODbDataFieldFilter):OINODbDataField[] {
179
- let result:OINODbDataField[] = []
180
- for (let f of this.fields) {
181
- if (filter(f)) {
182
- result.push(f)
183
- }
184
- }
185
- return result
186
- }
187
-
188
- /**
189
- * Return the primary key values of one row in order of the data model
190
- *
191
- * @param row data row
192
- * @param hashidValues apply hashid when applicable
193
- *
194
- */
195
- getRowPrimarykeyValues(row: OINODataRow, hashidValues:boolean = false): string[] {
196
- let values: string[] = [];
197
- for (let i=0; i< this.fields.length; i++) {
198
- const f = this.fields[i]
199
- if (f.fieldParams.isPrimaryKey) {
200
- const value:string = row[i]?.toString() || ""
201
- if (hashidValues && value && (f instanceof OINONumberDataField) && this.api.hashid) {
202
- values.push(this.api.hashid.encode(value))
203
- } else {
204
- values.push(value)
205
- }
206
- }
207
- }
208
- return values
209
- }
210
-
211
- /**
212
- * Print debug information about the fields.
213
- *
214
- * @param separator string to separate field prints
215
- *
216
- */
217
- printDebug(separator:string = ""): string {
218
- let result: string = this.api.params.tableName + ":" + separator;
219
- for (let f of this.fields) {
220
- result += f.printColumnDebug() + separator;
221
- }
222
- return result;
223
- }
224
-
225
- /**
226
- * Print all public properties (db, table name, fields) of the datamodel. Used
227
- * in automated testing validate schema has stayed the same.
228
- *
229
- */
230
- printFieldPublicPropertiesJson():string {
231
- const result:string = JSON.stringify(this.fields, (key:any, value:any) => {
232
- if (key.startsWith("_")) {
233
- return undefined
234
- } else {
235
- return value
236
- }
237
- })
238
- return result
239
- }
240
-
241
- /**
242
- * Print SQL select statement using optional id and filter.
243
- *
244
- * @param id OINO ID (i.e. combined primary key values)
245
- * @param params OINO reqest params
246
- *
247
- */
248
- printSqlSelect(id: string, params:OINODbSqlParams): string {
249
- let column_names = ""
250
- if (params.aggregate) {
251
- column_names = params.aggregate.printSqlColumnNames(this, params.select)
252
- } else {
253
- column_names = this._printSqlColumnNames(params.select)
254
- }
255
- const order_sql = params.order?.toSql(this) || ""
256
- const limit_sql = params.limit?.toSql(this) || ""
257
- const filter_sql = params.filter?.toSql(this) || ""
258
- const groupby_sql = params.aggregate?.toSql(this, params.select) || ""
259
-
260
- let where_sql = ""
261
- if ((id != null) && (id != "") && (filter_sql != "")) {
262
- where_sql = this._printSqlPrimaryKeyCondition(id) + " AND " + filter_sql
263
- } else if ((id != null) && (id != "")) {
264
- where_sql = this._printSqlPrimaryKeyCondition(id)
265
- } else if (filter_sql != "") {
266
- where_sql = filter_sql
267
- }
268
- const result = this.api.db.printSqlSelect(this.api.params.tableName, column_names, where_sql, order_sql, limit_sql, groupby_sql)
269
- return result;
270
- }
271
-
272
- /**
273
- * Print SQL insert statement from one data row.
274
- *
275
- * @param row one row of data in the data model
276
- *
277
- */
278
- printSqlInsert(row: OINODataRow): string {
279
- const table_name = this.api.db.printSqlTablename(this.api.params.tableName)
280
- const [columns, values] = this._printSqlInsertColumnsAndValues(row)
281
- const return_fields = this.api.params.returnInsertedIds ? this._printSqlPrimaryKeyColumns() : undefined
282
- return this.api.db.printSqlInsert(table_name, columns, values, return_fields);
283
- }
284
-
285
- /**
286
- * Print SQL insert statement from one data row.
287
- *
288
- * @param id OINO ID (i.e. combined primary key values)
289
- * @param row one row of data in the data model
290
- *
291
- */
292
- printSqlUpdate(id: string, row: OINODataRow): string {
293
- let result: string = "UPDATE " + this.api.db.printSqlTablename(this.api.params.tableName) + " SET " + this._printSqlUpdateValues(row) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
294
- return result;
295
- }
296
-
297
- /**
298
- * Print SQL delete statement for id.
299
- *
300
- * @param id OINO ID (i.e. combined primary key values)
301
- *
302
- */
303
- printSqlDelete(id: string): string {
304
- let result: string = "DELETE FROM " + this.api.db.printSqlTablename(this.api.params.tableName) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
305
- return result;
306
- }
307
- }
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import { OINO_ERROR_PREFIX, OINODataModel, OINODataField, OINODataRow, OINOConfig, OINOQuerySelect, OINOQueryParams, OINONumberDataField } from "@oino-ts/common"
8
+ import { OINODB_UNDEFINED } from "./OINODbConstants.js"
9
+ import { OINODbApi } from "./OINODbApi.js"
10
+ import { OINODbQueryOrder, OINODbQueryFilter, OINODbQueryLimit, OINODbQueryAggregate } from "./OINODbQueryParams.js"
11
+
12
+ /**
13
+ * OINO Datamodel object for representing one database table and it's columns.
14
+ *
15
+ */
16
+ export class OINODbDataModel extends OINODataModel {
17
+
18
+ /** Database refererence of the table */
19
+ readonly dbApi:OINODbApi
20
+
21
+ /** Field refererences of the API */
22
+ readonly fields: OINODataField[]
23
+
24
+ /**
25
+ * Constructor of the data model.
26
+ * NOTE! OINODbDataModel.initialize must be called after constructor to populate fields.
27
+ *
28
+ * @param api api of the data model
29
+ *
30
+ */
31
+ constructor(api:OINODbApi) {
32
+ super(api)
33
+ this.dbApi = api
34
+ this.fields = []
35
+ }
36
+
37
+ private _printColumnNames(select?:OINOQuerySelect): string {
38
+ let result: string = "";
39
+ for (let i=0; i < this.fields.length; i++) {
40
+ const f:OINODataField = this.fields[i]
41
+ if ((select?.isSelected(f.name) === false) && (f.fieldParams.isPrimaryKey == false)) { // if a field is not selected, we include a constant and correct fieldname instead so that dimensions of the data don't change but no unnecessary data is fetched
42
+ result += f.datasource.printStringValue(OINODB_UNDEFINED) + " as " + f.printColumnName()+","
43
+ } else {
44
+ result += f.printColumnName()+","
45
+ }
46
+ }
47
+ return result.substring(0, result.length-1)
48
+ }
49
+
50
+ private _printSqlInsertColumnsAndValues(row: OINODataRow): [string, string] {
51
+ let columns: string = "";
52
+ let values: string = "";
53
+ for (let i=0; i< this.fields.length; i++) {
54
+ const val = row[i];
55
+ // console.log("_printSqlInsertColumnsAndValues: row[" + i + "]=" + val)
56
+ if (val !== undefined) {
57
+ const f = this.fields[i]
58
+ if (values != "") {
59
+ columns += ",";
60
+ values += ",";
61
+ }
62
+ columns += f.printColumnName();
63
+ values += f.printCellAsValue(val);
64
+ }
65
+ }
66
+ // console.log("_printSqlInsertColumnsAndValues: columns=" + columns + ", values=" + values)
67
+ return [ columns, values ]
68
+ }
69
+
70
+ private _printSqlUpdateValues(row: OINODataRow): string {
71
+ let result: string = "";
72
+ for (let i=0; i< this.fields.length; i++) {
73
+ const f = this.fields[i]
74
+ const val = row[i];
75
+ if ((!f.fieldParams.isPrimaryKey) && (val !== undefined)) {
76
+ if (result != "") {
77
+ result += ",";
78
+ }
79
+ result += f.printColumnName() + "=" + f.printCellAsValue(val);
80
+ }
81
+ }
82
+ if (result == "") {
83
+ throw new Error(OINO_ERROR_PREFIX + ": no valid updatable fields provided for row!")
84
+ }
85
+ return result;
86
+ }
87
+
88
+ private _printSqlPrimaryKeyCondition(id_value: string): string {
89
+ let result: string = ""
90
+ let i:number = 0
91
+ const id_parts = id_value.split(OINOConfig.OINO_ID_SEPARATOR)
92
+ for (let f of this.fields) {
93
+ if (f.fieldParams.isPrimaryKey) {
94
+ if (result != "") {
95
+ result += " AND "
96
+ }
97
+ let value = decodeURIComponent(id_parts[i])
98
+ if ((f instanceof OINONumberDataField) && (this.dbApi.hashid)) {
99
+ value = this.dbApi.hashid.decode(value)
100
+ }
101
+ value = f.printCellAsValue(value)
102
+ if (value == "") { // ids are user input and could be specially crafted to be empty
103
+ throw new Error(OINO_ERROR_PREFIX + ": empty condition for id '" + id_value + "' for table " + this.api.params.tableName)
104
+ }
105
+ result += f.printColumnName() + "=" + value;
106
+ i = i + 1
107
+ }
108
+ }
109
+ if (i != id_parts.length) {
110
+ throw new Error(OINO_ERROR_PREFIX + ": id '" + id_value + "' is not a valid key for table " + this.api.params.tableName)
111
+ }
112
+ return "(" + result + ")";
113
+ }
114
+
115
+ private _printSqlPrimaryKeyColumns(): string[] {
116
+ let result: string[] = []
117
+ for (let f of this.fields) {
118
+ if (f.fieldParams.isPrimaryKey) {
119
+ result.push(this.dbApi.db.printColumnName(f.name))
120
+ }
121
+ }
122
+ return result
123
+ }
124
+
125
+ /**
126
+ * Print SQL select statement using optional id and filter.
127
+ *
128
+ * @param id OINO ID (i.e. combined primary key values)
129
+ * @param params OINO reqest params
130
+ *
131
+ */
132
+ printSqlSelect(id: string, params:OINOQueryParams): string {
133
+ let column_names = ""
134
+ if (params.aggregate) {
135
+ column_names = OINODbQueryAggregate.printColumnNames(params.aggregate, this, params.select)
136
+ } else {
137
+ column_names = this._printColumnNames(params.select)
138
+ }
139
+ const order_sql = params.order ? OINODbQueryOrder.printSql(params.order, this) : ""
140
+ const limit_sql = params.limit ? OINODbQueryLimit.printSql(params.limit, this) : ""
141
+ const filter_sql = params.filter ? OINODbQueryFilter.printSql(params.filter, this) : ""
142
+ const groupby_sql = params.aggregate ? OINODbQueryAggregate.printSql(params.aggregate, this, params.select) : ""
143
+
144
+ let where_sql = ""
145
+ if ((id != null) && (id != "") && (filter_sql != "")) {
146
+ where_sql = this._printSqlPrimaryKeyCondition(id) + " AND " + filter_sql
147
+ } else if ((id != null) && (id != "")) {
148
+ where_sql = this._printSqlPrimaryKeyCondition(id)
149
+ } else if (filter_sql != "") {
150
+ where_sql = filter_sql
151
+ }
152
+ const result = this.dbApi.db.printSqlSelect(this.api.params.tableName, column_names, where_sql, order_sql, limit_sql, groupby_sql)
153
+ return result;
154
+ }
155
+
156
+ /**
157
+ * Print SQL insert statement from one data row.
158
+ *
159
+ * @param row one row of data in the data model
160
+ *
161
+ */
162
+ printSqlInsert(row: OINODataRow): string {
163
+ const table_name = this.dbApi.db.printTableName(this.api.params.tableName)
164
+ const [columns, values] = this._printSqlInsertColumnsAndValues(row)
165
+ const return_fields = this.api.params.returnInsertedIds ? this._printSqlPrimaryKeyColumns() : undefined
166
+ return this.dbApi.db.printSqlInsert(table_name, columns, values, return_fields);
167
+ }
168
+
169
+ /**
170
+ * Print SQL insert statement from one data row.
171
+ *
172
+ * @param id OINO ID (i.e. combined primary key values)
173
+ * @param row one row of data in the data model
174
+ *
175
+ */
176
+ printSqlUpdate(id: string, row: OINODataRow): string {
177
+ let result: string = "UPDATE " + this.dbApi.db.printTableName(this.api.params.tableName) + " SET " + this._printSqlUpdateValues(row) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
178
+ return result;
179
+ }
180
+
181
+ /**
182
+ * Print SQL delete statement for id.
183
+ *
184
+ * @param id OINO ID (i.e. combined primary key values)
185
+ *
186
+ */
187
+ printSqlDelete(id: string): string {
188
+ let result: string = "DELETE FROM " + this.dbApi.db.printTableName(this.api.params.tableName) + " WHERE " + this._printSqlPrimaryKeyCondition(id) + ";";
189
+ return result;
190
+ }
191
+ }