@oino-ts/db-bunsqlite 0.0.16 → 0.0.17

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
@@ -59,20 +59,8 @@
59
59
 
60
60
  ## RESTfull
61
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
- ### HTTP GET
64
- ```
65
- Request and response:
66
- > curl.exe -X GET http://localhost:3001/orderdetails/11077:77
67
- [
68
- {"_OINOID_":"11077:77","OrderID":11077,"ProductID":77,"UnitPrice":13,"Quantity":2,"Discount":0}
69
- ]
70
-
71
- SQL:
72
- SELECT "OrderID","ProductID","UnitPrice","Quantity","Discount" FROM [OrderDetails] WHERE ("OrderID"=11077 AND "ProductID"=77);
73
- ```
74
62
 
75
- ### HTTP POST
63
+ For example HTTP POST
76
64
  ```
77
65
  Request and response:
78
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}]'
@@ -82,31 +70,12 @@
82
70
  INSERT INTO [OrderDetails] ("OrderID","ProductID","UnitPrice","Quantity","Discount") VALUES (11077,99,19,1,0);
83
71
  ```
84
72
 
85
- ### HTTP PUT
86
- ```
87
- Request and response:
88
- > curl.exe -X PUT http://localhost:3001/orderdetails/11077:99 -H "Content-Type: application/json" --data '[{\"UnitPrice\":20}]'
89
- {"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
90
-
91
- SQL:
92
- UPDATE [OrderDetails] SET "UnitPrice"=20 WHERE ("OrderID"=11077 AND "ProductID"=99);
93
- ```
94
-
95
- ### HTTP DELETE
96
- ```
97
- Request and response:
98
- > curl.exe -X DELETE http://localhost:3001/orderdetails/11077:99
99
- {"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
100
73
 
101
- SQL:
102
- DELETE FROM [OrderDetails] WHERE ("OrderID"=11077 AND "ProductID"=99);
103
- ```
104
-
105
74
  ## Universal Serialization
106
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.
107
76
 
108
77
  ### Features
109
- - Files can be sent to BLOB fields using BASE64 encoding.
78
+ - Files can be sent to BLOB fields using BASE64 or MIME multipart encoding.
110
79
  - Datetimes are (optionally) normalized to ISO 8601 format.
111
80
  - Extended JSON-encoding
112
81
  - 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).
@@ -135,7 +104,7 @@
135
104
  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`.
136
105
 
137
106
  ## Power Of SQL
138
- Since OINO is just generating 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.
107
+ Since OINO is just generating SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html), order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.html) and limits/paging with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlLimit.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.
139
108
 
140
109
  ## Swagger Support
141
110
  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.
@@ -179,8 +148,8 @@
179
148
  ### Batch updates
180
149
  Supporting batch updates similar to batch inserts is slightly bending the RESTfull principles but would still be a useful optional feature.
181
150
 
182
- ### Aggregation and limits
183
- Similar to filtering and ordering, aggregation and limits can be implemented as HTTP request parameters telling what column is aggregated or used for ordering or how many results to return.
151
+ ### Aggregation
152
+ Similar to filtering, ordering and limits, aggregation could be implemented as HTTP request parameters telling what column is aggregated or used for ordering or how many results to return.
184
153
 
185
154
  ### Streaming
186
155
  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.
@@ -222,4 +191,3 @@
222
191
 
223
192
  ## SQL Scripts
224
193
  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).
225
-
@@ -24,6 +24,7 @@ class OINOBunSqliteDataset extends db_1.OINODbMemoryDataSet {
24
24
  class OINODbBunSqlite extends db_1.OINODb {
25
25
  static _tableDescriptionRegex = /^CREATE TABLE\s*[\"\[]?\w+[\"\]]?\s*\(\s*(.*)\s*\)\s*(WITHOUT ROWID)?$/msi;
26
26
  static _tablePrimarykeyRegex = /PRIMARY KEY \(([^\)]+)\)/i;
27
+ static _tableForeignkeyRegex = /FOREIGN KEY \(\[([^\)]+)\]\)/i;
27
28
  static _tableFieldTypeRegex = /[\"\[\s]?(\w+)[\"\]\s]\s?(INTEGER|REAL|DOUBLE|NUMERIC|DECIMAL|TEXT|BLOB|VARCHAR|DATETIME|DATE|BOOLEAN)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i;
28
29
  _db;
29
30
  /**
@@ -44,6 +45,7 @@ class OINODbBunSqlite extends db_1.OINODb {
44
45
  _parseDbFieldParams(fieldStr) {
45
46
  const result = {
46
47
  isPrimaryKey: fieldStr.indexOf("PRIMARY KEY") >= 0,
48
+ isForeignKey: false,
47
49
  isAutoInc: fieldStr.indexOf("AUTOINCREMENT") >= 0,
48
50
  isNotNull: fieldStr.indexOf("NOT NULL") >= 0
49
51
  };
@@ -191,6 +193,7 @@ class OINODbBunSqlite extends db_1.OINODb {
191
193
  async initializeApiDatamodel(api) {
192
194
  const res = await this.sqlSelect("select sql from sqlite_schema WHERE name='" + api.params.tableName + "'");
193
195
  const sql_desc = (res?.getRow()[0]);
196
+ const foreign_keys = [];
194
197
  // OINOLog.debug("OINODbBunSqlite.initDatamodel.sql_desc=" + sql_desc)
195
198
  let table_matches = OINODbBunSqlite._tableDescriptionRegex.exec(sql_desc);
196
199
  // OINOLog.debug("OINODbBunSqlite.initDatamodel", {table_matches:table_matches})
@@ -208,7 +211,8 @@ class OINODbBunSqlite extends db_1.OINODb {
208
211
  // OINOLog.debug("initDatamodel next field", {field_str:field_str, field_match:field_match, field_params:field_params})
209
212
  if ((!field_match) || (field_match.length < 3)) {
210
213
  let primarykey_match = OINODbBunSqlite._tablePrimarykeyRegex.exec(field_str);
211
- // OINOLog.debug("initDatamodel non-field definition", {primarykey_match:primarykey_match})
214
+ let foreignkey_match = OINODbBunSqlite._tableForeignkeyRegex.exec(field_str);
215
+ db_1.OINOLog.debug("initDatamodel non-field definition", { primarykey_match: primarykey_match, foreignkey_match: foreignkey_match });
212
216
  if (primarykey_match && primarykey_match.length >= 2) {
213
217
  const primary_keys = primarykey_match[1].replaceAll("\"", "").split(','); // not sure if will have space or not so split by comma and trim later
214
218
  for (let i = 0; i < primary_keys.length; i++) {
@@ -220,6 +224,14 @@ class OINODbBunSqlite extends db_1.OINODb {
220
224
  }
221
225
  }
222
226
  }
227
+ else if (foreignkey_match && foreignkey_match.length >= 2) {
228
+ const fk = foreignkey_match[1].trim();
229
+ for (let j = 0; j < api.datamodel.fields.length; j++) {
230
+ if (api.datamodel.fields[j].name == fk) {
231
+ api.datamodel.fields[j].fieldParams.isForeignKey = true;
232
+ }
233
+ }
234
+ }
223
235
  else {
224
236
  db_1.OINOLog.info("OINODbBunSqlite.initializeApiDatamodel: Unsupported field definition skipped.", { field: field_str });
225
237
  }
@@ -21,6 +21,7 @@ class OINOBunSqliteDataset extends OINODbMemoryDataSet {
21
21
  export class OINODbBunSqlite extends OINODb {
22
22
  static _tableDescriptionRegex = /^CREATE TABLE\s*[\"\[]?\w+[\"\]]?\s*\(\s*(.*)\s*\)\s*(WITHOUT ROWID)?$/msi;
23
23
  static _tablePrimarykeyRegex = /PRIMARY KEY \(([^\)]+)\)/i;
24
+ static _tableForeignkeyRegex = /FOREIGN KEY \(\[([^\)]+)\]\)/i;
24
25
  static _tableFieldTypeRegex = /[\"\[\s]?(\w+)[\"\]\s]\s?(INTEGER|REAL|DOUBLE|NUMERIC|DECIMAL|TEXT|BLOB|VARCHAR|DATETIME|DATE|BOOLEAN)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i;
25
26
  _db;
26
27
  /**
@@ -41,6 +42,7 @@ export class OINODbBunSqlite extends OINODb {
41
42
  _parseDbFieldParams(fieldStr) {
42
43
  const result = {
43
44
  isPrimaryKey: fieldStr.indexOf("PRIMARY KEY") >= 0,
45
+ isForeignKey: false,
44
46
  isAutoInc: fieldStr.indexOf("AUTOINCREMENT") >= 0,
45
47
  isNotNull: fieldStr.indexOf("NOT NULL") >= 0
46
48
  };
@@ -188,6 +190,7 @@ export class OINODbBunSqlite extends OINODb {
188
190
  async initializeApiDatamodel(api) {
189
191
  const res = await this.sqlSelect("select sql from sqlite_schema WHERE name='" + api.params.tableName + "'");
190
192
  const sql_desc = (res?.getRow()[0]);
193
+ const foreign_keys = [];
191
194
  // OINOLog.debug("OINODbBunSqlite.initDatamodel.sql_desc=" + sql_desc)
192
195
  let table_matches = OINODbBunSqlite._tableDescriptionRegex.exec(sql_desc);
193
196
  // OINOLog.debug("OINODbBunSqlite.initDatamodel", {table_matches:table_matches})
@@ -205,7 +208,8 @@ export class OINODbBunSqlite extends OINODb {
205
208
  // OINOLog.debug("initDatamodel next field", {field_str:field_str, field_match:field_match, field_params:field_params})
206
209
  if ((!field_match) || (field_match.length < 3)) {
207
210
  let primarykey_match = OINODbBunSqlite._tablePrimarykeyRegex.exec(field_str);
208
- // OINOLog.debug("initDatamodel non-field definition", {primarykey_match:primarykey_match})
211
+ let foreignkey_match = OINODbBunSqlite._tableForeignkeyRegex.exec(field_str);
212
+ OINOLog.debug("initDatamodel non-field definition", { primarykey_match: primarykey_match, foreignkey_match: foreignkey_match });
209
213
  if (primarykey_match && primarykey_match.length >= 2) {
210
214
  const primary_keys = primarykey_match[1].replaceAll("\"", "").split(','); // not sure if will have space or not so split by comma and trim later
211
215
  for (let i = 0; i < primary_keys.length; i++) {
@@ -217,6 +221,14 @@ export class OINODbBunSqlite extends OINODb {
217
221
  }
218
222
  }
219
223
  }
224
+ else if (foreignkey_match && foreignkey_match.length >= 2) {
225
+ const fk = foreignkey_match[1].trim();
226
+ for (let j = 0; j < api.datamodel.fields.length; j++) {
227
+ if (api.datamodel.fields[j].name == fk) {
228
+ api.datamodel.fields[j].fieldParams.isForeignKey = true;
229
+ }
230
+ }
231
+ }
220
232
  else {
221
233
  OINOLog.info("OINODbBunSqlite.initializeApiDatamodel: Unsupported field definition skipped.", { field: field_str });
222
234
  }
@@ -6,6 +6,7 @@ import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINODataCell } from "@o
6
6
  export declare class OINODbBunSqlite extends OINODb {
7
7
  private static _tableDescriptionRegex;
8
8
  private static _tablePrimarykeyRegex;
9
+ private static _tableForeignkeyRegex;
9
10
  private static _tableFieldTypeRegex;
10
11
  private _db;
11
12
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oino-ts/db-bunsqlite",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "OINO TS package for using Bun Sqlite databases.",
5
5
  "author": "Matias Kiviniemi (pragmatta)",
6
6
  "license": "MPL-2.0",
@@ -20,7 +20,7 @@
20
20
  "module": "./dist/esm/index.js",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "dependencies": {
23
- "@oino-ts/db": "^0.0.16"
23
+ "@oino-ts/db": "^0.0.17"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^20.12.7",
@@ -25,6 +25,7 @@ class OINOBunSqliteDataset extends OINODbMemoryDataSet {
25
25
  export class OINODbBunSqlite extends OINODb {
26
26
  private static _tableDescriptionRegex = /^CREATE TABLE\s*[\"\[]?\w+[\"\]]?\s*\(\s*(.*)\s*\)\s*(WITHOUT ROWID)?$/msi
27
27
  private static _tablePrimarykeyRegex = /PRIMARY KEY \(([^\)]+)\)/i
28
+ private static _tableForeignkeyRegex = /FOREIGN KEY \(\[([^\)]+)\]\)/i
28
29
  private static _tableFieldTypeRegex = /[\"\[\s]?(\w+)[\"\]\s]\s?(INTEGER|REAL|DOUBLE|NUMERIC|DECIMAL|TEXT|BLOB|VARCHAR|DATETIME|DATE|BOOLEAN)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i
29
30
 
30
31
  private _db:BunSqliteDb|null
@@ -49,6 +50,7 @@ export class OINODbBunSqlite extends OINODb {
49
50
  private _parseDbFieldParams(fieldStr:string): OINODbDataFieldParams {
50
51
  const result:OINODbDataFieldParams = {
51
52
  isPrimaryKey: fieldStr.indexOf("PRIMARY KEY") >= 0,
53
+ isForeignKey: false,
52
54
  isAutoInc: fieldStr.indexOf("AUTOINCREMENT") >= 0,
53
55
  isNotNull: fieldStr.indexOf("NOT NULL") >= 0
54
56
  }
@@ -203,6 +205,7 @@ export class OINODbBunSqlite extends OINODb {
203
205
  async initializeApiDatamodel(api:OINODbApi): Promise<void> {
204
206
  const res:OINODbDataSet|null = await this.sqlSelect("select sql from sqlite_schema WHERE name='" + api.params.tableName + "'")
205
207
  const sql_desc:string = (res?.getRow()[0]) as string
208
+ const foreign_keys:string[] = []
206
209
  // OINOLog.debug("OINODbBunSqlite.initDatamodel.sql_desc=" + sql_desc)
207
210
  let table_matches = OINODbBunSqlite._tableDescriptionRegex.exec(sql_desc)
208
211
  // OINOLog.debug("OINODbBunSqlite.initDatamodel", {table_matches:table_matches})
@@ -220,7 +223,8 @@ export class OINODbBunSqlite extends OINODb {
220
223
  // OINOLog.debug("initDatamodel next field", {field_str:field_str, field_match:field_match, field_params:field_params})
221
224
  if ((!field_match) || (field_match.length < 3)) {
222
225
  let primarykey_match = OINODbBunSqlite._tablePrimarykeyRegex.exec(field_str)
223
- // OINOLog.debug("initDatamodel non-field definition", {primarykey_match:primarykey_match})
226
+ let foreignkey_match = OINODbBunSqlite._tableForeignkeyRegex.exec(field_str)
227
+ OINOLog.debug("initDatamodel non-field definition", {primarykey_match:primarykey_match, foreignkey_match:foreignkey_match})
224
228
  if (primarykey_match && primarykey_match.length >= 2) {
225
229
  const primary_keys:string[] = primarykey_match[1].replaceAll("\"", "").split(',') // not sure if will have space or not so split by comma and trim later
226
230
  for (let i:number=0; i<primary_keys.length; i++) {
@@ -232,6 +236,14 @@ export class OINODbBunSqlite extends OINODb {
232
236
  }
233
237
  }
234
238
 
239
+ } else if (foreignkey_match && foreignkey_match.length >= 2) {
240
+ const fk:string = foreignkey_match[1].trim()
241
+ for (let j:number=0; j<api.datamodel.fields.length; j++) {
242
+ if (api.datamodel.fields[j].name == fk) {
243
+ api.datamodel.fields[j].fieldParams.isForeignKey = true
244
+ }
245
+ }
246
+
235
247
  } else {
236
248
  OINOLog.info("OINODbBunSqlite.initializeApiDatamodel: Unsupported field definition skipped.", { field: field_str })
237
249
  }