@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.
- package/README.md +222 -0
- package/dist/cjs/OINODb.js +27 -0
- package/dist/cjs/OINODbApi.js +270 -0
- package/dist/cjs/OINODbConfig.js +86 -0
- package/dist/cjs/OINODbDataField.js +354 -0
- package/dist/cjs/OINODbDataModel.js +279 -0
- package/dist/cjs/OINODbDataSet.js +139 -0
- package/dist/cjs/OINODbFactory.js +563 -0
- package/dist/cjs/OINODbModelSet.js +267 -0
- package/dist/cjs/OINODbParams.js +280 -0
- package/dist/cjs/OINODbRequestParams.js +280 -0
- package/dist/cjs/OINODbSwagger.js +201 -0
- package/dist/cjs/index.js +51 -0
- package/dist/esm/OINODb.js +23 -0
- package/dist/esm/OINODbApi.js +265 -0
- package/dist/esm/OINODbConfig.js +82 -0
- package/dist/esm/OINODbDataField.js +345 -0
- package/dist/esm/OINODbDataModel.js +275 -0
- package/dist/esm/OINODbDataSet.js +134 -0
- package/dist/esm/OINODbFactory.js +559 -0
- package/dist/esm/OINODbModelSet.js +263 -0
- package/dist/esm/OINODbRequestParams.js +274 -0
- package/dist/esm/OINODbSwagger.js +197 -0
- package/dist/esm/index.js +17 -0
- package/dist/types/OINODb.d.ts +75 -0
- package/dist/types/OINODbApi.d.ts +57 -0
- package/dist/types/OINODbConfig.d.ts +52 -0
- package/dist/types/OINODbDataField.d.ts +202 -0
- package/dist/types/OINODbDataModel.d.ts +108 -0
- package/dist/types/OINODbDataSet.d.ts +95 -0
- package/dist/types/OINODbFactory.d.ts +99 -0
- package/dist/types/OINODbModelSet.d.ts +50 -0
- package/dist/types/OINODbRequestParams.d.ts +130 -0
- package/dist/types/OINODbSwagger.d.ts +25 -0
- package/dist/types/index.d.ts +103 -0
- package/package.json +35 -0
- package/src/OINODb.ts +98 -0
- package/src/OINODbApi.test.ts +243 -0
- package/src/OINODbApi.ts +270 -0
- package/src/OINODbConfig.ts +92 -0
- package/src/OINODbDataField.ts +372 -0
- package/src/OINODbDataModel.ts +290 -0
- package/src/OINODbDataSet.ts +170 -0
- package/src/OINODbFactory.ts +570 -0
- package/src/OINODbModelSet.ts +286 -0
- package/src/OINODbRequestParams.ts +281 -0
- package/src/OINODbSwagger.ts +209 -0
- package/src/index.ts +116 -0
|
@@ -0,0 +1,286 @@
|
|
|
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 { OINODbDataSet, OINODbDataModel, OINODbDataField, OINODataRow, OINOContentType, OINOBlobDataField, OINOStr, OINODbConfig, OINONumberDataField, OINOBooleanDataField, OINODataCell, OINOLog } from "./index.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Class for dataset based on a data model that can be serialized to
|
|
11
|
+
* a supported format:
|
|
12
|
+
* - JSON (application/json)
|
|
13
|
+
* - CSV (text/csv)
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export class OINODbModelSet {
|
|
17
|
+
|
|
18
|
+
/** Reference to datamodel */
|
|
19
|
+
readonly datamodel: OINODbDataModel
|
|
20
|
+
|
|
21
|
+
/** Reference to data set */
|
|
22
|
+
readonly dataset: OINODbDataSet
|
|
23
|
+
|
|
24
|
+
/** Collection of errors */
|
|
25
|
+
errors: string[]
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Constructor for `OINODbModelSet`.
|
|
29
|
+
*
|
|
30
|
+
* @param datamodel data model
|
|
31
|
+
* @param dataset data set
|
|
32
|
+
*/
|
|
33
|
+
constructor(datamodel: OINODbDataModel, dataset: OINODbDataSet) {
|
|
34
|
+
this.datamodel = datamodel
|
|
35
|
+
this.dataset = dataset
|
|
36
|
+
this.errors = this.dataset.messages
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private _encodeAndHashFieldValue(field:OINODbDataField, value:string|null, contentType:OINOContentType, primaryKeyValues:string[], rowIdSeed:string) {
|
|
40
|
+
if (field.fieldParams.isPrimaryKey) {
|
|
41
|
+
if (value && (field instanceof OINONumberDataField) && (this.datamodel.api.hashid)) {
|
|
42
|
+
value = this.datamodel.api.hashid.encode(value, rowIdSeed)
|
|
43
|
+
}
|
|
44
|
+
primaryKeyValues.push(value || "")
|
|
45
|
+
}
|
|
46
|
+
value = OINOStr.encode(value, contentType)
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private _writeRowJson(row:OINODataRow):string {
|
|
51
|
+
// console.log("OINODbModelSet._writeRowJson: row=" + row)
|
|
52
|
+
const model:OINODbDataModel = this.datamodel
|
|
53
|
+
const fields:OINODbDataField[] = model.fields
|
|
54
|
+
let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
|
|
55
|
+
let primary_key_values:string[] = []
|
|
56
|
+
let json_row:string = ""
|
|
57
|
+
for (let i=0; i<fields.length; i++) {
|
|
58
|
+
const f = fields[i]
|
|
59
|
+
let value:string|null|undefined = f.serializeCell(row[i])
|
|
60
|
+
if (value === undefined) {
|
|
61
|
+
OINOLog.info("OINODbModelSet._writeRowJson: undefined value skipped", {field_name:f.name})
|
|
62
|
+
|
|
63
|
+
} else if (value === null) {
|
|
64
|
+
json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":null"
|
|
65
|
+
|
|
66
|
+
} else {
|
|
67
|
+
|
|
68
|
+
let is_hashed:boolean = f.fieldParams.isPrimaryKey && (f instanceof OINONumberDataField) && (this.datamodel.api.hashid != null)
|
|
69
|
+
let is_value = (f instanceof OINOBooleanDataField) || ((f instanceof OINONumberDataField) && !is_hashed)
|
|
70
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.json, primary_key_values, f.name + " " + row_id_seed)
|
|
71
|
+
if (is_value) {
|
|
72
|
+
value = value.substring(1, value.length-1)
|
|
73
|
+
}
|
|
74
|
+
json_row += "," + OINOStr.encode(f.name, OINOContentType.json) + ":" + value
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
json_row = OINOStr.encode(OINODbConfig.OINODB_ID_FIELD, OINOContentType.json) + ":" + OINOStr.encode(OINODbConfig.printOINOId(primary_key_values), OINOContentType.json) + json_row
|
|
78
|
+
// OINOLog_debug("OINODbModelSet._writeRowJson="+json_row)
|
|
79
|
+
return "{" + json_row + "}"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private _writeStringJson():string {
|
|
83
|
+
let result:string = ""
|
|
84
|
+
while (!this.dataset.isEof()) {
|
|
85
|
+
if (result != "") {
|
|
86
|
+
result += ",\r\n"
|
|
87
|
+
}
|
|
88
|
+
result += this._writeRowJson(this.dataset.getRow())
|
|
89
|
+
this.dataset.next()
|
|
90
|
+
}
|
|
91
|
+
result = "[\r\n" + result + "\r\n]"
|
|
92
|
+
// OINOLog_debug("OINODbModelSet._writeStringJson="+result)
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private _writeHeaderCsv():string {
|
|
97
|
+
const model:OINODbDataModel = this.datamodel
|
|
98
|
+
const fields:OINODbDataField[] = model.fields
|
|
99
|
+
let csv_header:string = "\"" + OINODbConfig.OINODB_ID_FIELD + "\""
|
|
100
|
+
for (let i=0; i<fields.length; i++) {
|
|
101
|
+
csv_header += ",\"" + fields[i].name + "\""
|
|
102
|
+
}
|
|
103
|
+
// OINOLog_debug("OINODbModelSet._writeHeaderCsv="+csv_header)
|
|
104
|
+
return csv_header
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private _writeRowCsv(row:OINODataRow):string {
|
|
108
|
+
// OINOLog_debug("OINODbModelSet._writeRowCsv", {row:row})
|
|
109
|
+
const model:OINODbDataModel = this.datamodel
|
|
110
|
+
const fields:OINODbDataField[] = model.fields
|
|
111
|
+
let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
|
|
112
|
+
let primary_key_values:string[] = []
|
|
113
|
+
let csv_row:string = ""
|
|
114
|
+
for (let i=0; i<fields.length; i++) {
|
|
115
|
+
const f = fields[i]
|
|
116
|
+
let value:string|null|undefined = f.serializeCell(row[i])
|
|
117
|
+
if (value == null) {
|
|
118
|
+
csv_row += "," + OINOStr.encode(value, OINOContentType.csv) // either null or undefined
|
|
119
|
+
|
|
120
|
+
} else {
|
|
121
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.csv, primary_key_values, f.name + " " + row_id_seed)
|
|
122
|
+
csv_row += "," + value
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
csv_row = OINOStr.encode(OINODbConfig.printOINOId(primary_key_values), OINOContentType.csv) + csv_row
|
|
126
|
+
// OINOLog_debug("OINODbModelSet._writeRowCsv="+csv_row)
|
|
127
|
+
return csv_row
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private _writeStringCsv():string {
|
|
131
|
+
let result:string = this._writeHeaderCsv()
|
|
132
|
+
while (!this.dataset.isEof()) {
|
|
133
|
+
if (result != "") {
|
|
134
|
+
result += "\r\n"
|
|
135
|
+
}
|
|
136
|
+
result += this._writeRowCsv(this.dataset.getRow())
|
|
137
|
+
this.dataset.next()
|
|
138
|
+
}
|
|
139
|
+
// OINOLog_debug("OINODbModelSet._writeStringCsv="+result)
|
|
140
|
+
return result
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private _writeRowFormdataParameterBlock(blockName:string, blockValue:string|null, multipartBoundary:string):string {
|
|
144
|
+
if (blockValue === null) {
|
|
145
|
+
return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n"
|
|
146
|
+
} else {
|
|
147
|
+
return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"\r\n\r\n" + blockValue + "\r\n"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private _writeRowFormdataFileBlock(blockName:string, blockValue:string, multipartBoundary:string):string {
|
|
152
|
+
return multipartBoundary + "\r\n" + "Content-Disposition: form-data; name=\"" + blockName + "\"; filename=" + blockName + "\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: BASE64\r\n\r\n" + blockValue + "\r\n"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private _writeRowFormdata(row:OINODataRow):string {
|
|
156
|
+
const multipart_boundary:string = "---------OINOMultipartBoundary35424568" // this method is just used for test data generation and we want it to be static
|
|
157
|
+
const model:OINODbDataModel = this.datamodel
|
|
158
|
+
const fields:OINODbDataField[] = model.fields
|
|
159
|
+
let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
|
|
160
|
+
let primary_key_values:string[] = []
|
|
161
|
+
let result:string = ""
|
|
162
|
+
for (let i=0; i<fields.length; i++) {
|
|
163
|
+
const f = fields[i]
|
|
164
|
+
let value:string|null|undefined = f.serializeCell(row[i])
|
|
165
|
+
let formdata_block:string = ""
|
|
166
|
+
let is_file = (f instanceof OINOBlobDataField)
|
|
167
|
+
|
|
168
|
+
if (value === undefined) {
|
|
169
|
+
OINOLog.info("OINODbModelSet._writeRowFormdata: undefined value skipped.", {field:f.name})
|
|
170
|
+
|
|
171
|
+
} else if (value === null) {
|
|
172
|
+
formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, null, multipart_boundary)
|
|
173
|
+
|
|
174
|
+
} else {
|
|
175
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.formdata, primary_key_values, f.name + " " + row_id_seed)
|
|
176
|
+
if (is_file) {
|
|
177
|
+
formdata_block = this._writeRowFormdataFileBlock(f.name, value, multipart_boundary)
|
|
178
|
+
} else {
|
|
179
|
+
formdata_block = this._writeRowFormdataParameterBlock(fields[i].name, value, multipart_boundary)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
// OINOLog.debug("OINODbModelSet._writeRowFormdata next block", {formdata_block:formdata_block})
|
|
185
|
+
result += formdata_block
|
|
186
|
+
}
|
|
187
|
+
result = this._writeRowFormdataParameterBlock(OINODbConfig.OINODB_ID_FIELD, OINODbConfig.printOINOId(primary_key_values), multipart_boundary) + result
|
|
188
|
+
return result
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private _writeStringFormdata():string {
|
|
192
|
+
let result:string = this._writeRowFormdata(this.dataset.getRow())
|
|
193
|
+
this.dataset.next()
|
|
194
|
+
if (!this.dataset.isEof()) {
|
|
195
|
+
OINOLog.warning("OINODbModelSet._writeStringUrlencode: content type " + OINOContentType.formdata + " does not mixed part content and only first row has been written!")
|
|
196
|
+
}
|
|
197
|
+
return result
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
private _writeRowUrlencode(row:OINODataRow):string {
|
|
202
|
+
// console.log("OINODbModelSet._writeRowCsv row=" + row)
|
|
203
|
+
const model:OINODbDataModel = this.datamodel
|
|
204
|
+
const fields:OINODbDataField[] = model.fields
|
|
205
|
+
let row_id_seed:string = model.getRowPrimarykeyValues(row).join(' ')
|
|
206
|
+
let primary_key_values:string[] = []
|
|
207
|
+
let urlencode_row:string = ""
|
|
208
|
+
for (let i=0; i<fields.length; i++) {
|
|
209
|
+
const f = fields[i]
|
|
210
|
+
let value:string|null|undefined = f.serializeCell(row[i])
|
|
211
|
+
if ((value === undefined)) { // || (value === null)) {
|
|
212
|
+
// console.log("OINODbModelSet._writeRowUrlencode undefined field value:" + fields[i].name)
|
|
213
|
+
} else {
|
|
214
|
+
value = this._encodeAndHashFieldValue(f, value, OINOContentType.urlencode, primary_key_values, f.name + " " + row_id_seed)
|
|
215
|
+
if (urlencode_row != "") {
|
|
216
|
+
urlencode_row += "&"
|
|
217
|
+
}
|
|
218
|
+
urlencode_row += OINOStr.encode(f.name, OINOContentType.urlencode) + "=" + value
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
urlencode_row = OINOStr.encode(OINODbConfig.OINODB_ID_FIELD, OINOContentType.urlencode) + "=" + OINOStr.encode(OINODbConfig.printOINOId(primary_key_values), OINOContentType.urlencode) + "&" + urlencode_row
|
|
222
|
+
// OINOLog_debug("OINODbModelSet._writeRowCsv="+csv_row)
|
|
223
|
+
return urlencode_row
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private _writeStringUrlencode():string {
|
|
227
|
+
let result:string = ""
|
|
228
|
+
let line_count = 0
|
|
229
|
+
while (!this.dataset.isEof()) {
|
|
230
|
+
result += this._writeRowUrlencode(this.dataset.getRow()) + "\r\n"
|
|
231
|
+
this.dataset.next()
|
|
232
|
+
line_count += 1
|
|
233
|
+
}
|
|
234
|
+
// OINOLog_debug("OINODbModelSet._writeStringCsv="+result)
|
|
235
|
+
if (line_count > 1) {
|
|
236
|
+
OINOLog.warning("OINODbModelSet._writeStringUrlencode: content type " + OINOContentType.urlencode + " does not officially support multiline content!")
|
|
237
|
+
}
|
|
238
|
+
return result
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Serialize model set in the given format.
|
|
243
|
+
*
|
|
244
|
+
* @param [contentType=OINOContentType.json] serialization content type
|
|
245
|
+
*
|
|
246
|
+
*/
|
|
247
|
+
writeString(contentType:OINOContentType = OINOContentType.json):string {
|
|
248
|
+
let result:string = ""
|
|
249
|
+
if (contentType == OINOContentType.csv) {
|
|
250
|
+
result += this._writeStringCsv()
|
|
251
|
+
|
|
252
|
+
} else if (contentType == OINOContentType.json) {
|
|
253
|
+
result += this._writeStringJson()
|
|
254
|
+
|
|
255
|
+
} else if (contentType == OINOContentType.formdata) {
|
|
256
|
+
result += this._writeStringFormdata()
|
|
257
|
+
|
|
258
|
+
} else if (contentType == OINOContentType.urlencode) {
|
|
259
|
+
result += this._writeStringUrlencode()
|
|
260
|
+
|
|
261
|
+
} else {
|
|
262
|
+
OINOLog.error("OINODbModelSet.writeString: content type is only for input!", {contentType:contentType})
|
|
263
|
+
}
|
|
264
|
+
return result
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get value of given field in the current row. Undefined if no rows,
|
|
269
|
+
* field not found or value does not exist.
|
|
270
|
+
*
|
|
271
|
+
* @param fieldName name of the field
|
|
272
|
+
*
|
|
273
|
+
*/
|
|
274
|
+
getValueByFieldName(fieldName:string):OINODataCell {
|
|
275
|
+
let result:OINODataCell = undefined
|
|
276
|
+
if (!this.dataset.isEof()) {
|
|
277
|
+
const current_row:OINODataRow = this.dataset.getRow()
|
|
278
|
+
const field_index:number = this.datamodel.findFieldIndexByName(fieldName)
|
|
279
|
+
if (field_index >= 0) {
|
|
280
|
+
result = current_row[field_index]
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return result
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
@@ -0,0 +1,281 @@
|
|
|
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 { OINOStr, OINODbDataField, OINODbDataModel, OINO_ERROR_PREFIX, OINOLog } from "./index.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Supported logical conjunctions in filter predicates.
|
|
11
|
+
* @enum
|
|
12
|
+
*/
|
|
13
|
+
export enum OINODbSqlBooleanOperation { and = "and", or = "or", not = "not" }
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Supported logical conjunctions in filter predicates.
|
|
17
|
+
* @enum
|
|
18
|
+
*/
|
|
19
|
+
export enum OINODbSqlComparison { lt = "lt", le = "le", eq = "eq", ge = "ge", gt = "gt", like = "like" }
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Class for recursively parsing of filters and printing them as SQL conditions.
|
|
23
|
+
* Supports three types of statements
|
|
24
|
+
* - comparison: (field)-lt|le|eq|ge|gt|like(value)
|
|
25
|
+
* - negation: -not(filter)
|
|
26
|
+
* - conjunction/disjunction: (filter)-and|or(filter)
|
|
27
|
+
* Supported conditions are comparisons (<, <=, =, >=, >) and substring match (LIKE).
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
export class OINODbSqlFilter {
|
|
31
|
+
private static _booleanOperationRegex = /^\s?\-(and|or)\s?$/i
|
|
32
|
+
private static _negationRegex = /^-(not|)\((.+)\)$/i
|
|
33
|
+
private static _filterComparisonRegex = /^\(([^'"\(\)]+)\)\s?\-(lt|le|eq|ge|gt|like)\s?\(([^'"\(\)]+)\)$/i
|
|
34
|
+
|
|
35
|
+
private _leftSide: OINODbSqlFilter | string
|
|
36
|
+
private _rightSide: OINODbSqlFilter | string
|
|
37
|
+
private _operator:OINODbSqlComparison|OINODbSqlBooleanOperation|null
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Constructor of `OINODbSqlFilter`
|
|
41
|
+
* @param leftSide left side of the filter, either another filter or a column name
|
|
42
|
+
* @param operation operation of the filter, either `OINODbSqlComparison` or `OINODbSqlBooleanOperation`
|
|
43
|
+
* @param rightSide right side of the filter, either another filter or a value
|
|
44
|
+
*/
|
|
45
|
+
constructor(leftSide:OINODbSqlFilter|string, operation:OINODbSqlComparison|OINODbSqlBooleanOperation|null, rightSide:OINODbSqlFilter|string) {
|
|
46
|
+
if (!(
|
|
47
|
+
((operation === null) && (leftSide == "") && (rightSide == "")) ||
|
|
48
|
+
((operation !== null) && (Object.values(OINODbSqlComparison).includes(operation as OINODbSqlComparison)) && (typeof(leftSide) == "string") && (leftSide != "") && (typeof(rightSide) == "string") && (rightSide != "")) ||
|
|
49
|
+
((operation == OINODbSqlBooleanOperation.not) && (leftSide == "") && (rightSide instanceof OINODbSqlFilter)) ||
|
|
50
|
+
(((operation == OINODbSqlBooleanOperation.and) || (operation == OINODbSqlBooleanOperation.or)) && (leftSide instanceof OINODbSqlFilter) && (rightSide instanceof OINODbSqlFilter))
|
|
51
|
+
)) {
|
|
52
|
+
OINOLog.debug("Unsupported OINODbSqlFilter format!", {leftSide:leftSide, operation:operation, rightSide:rightSide})
|
|
53
|
+
throw new Error(OINO_ERROR_PREFIX + ": Unsupported OINODbSqlFilter format!")
|
|
54
|
+
}
|
|
55
|
+
this._leftSide = leftSide
|
|
56
|
+
this._operator = operation
|
|
57
|
+
this._rightSide = rightSide
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Constructor for `OINOFilter` as parser of http parameter.
|
|
62
|
+
*
|
|
63
|
+
* @param filterString string representation of filter from HTTP-request
|
|
64
|
+
*
|
|
65
|
+
*/
|
|
66
|
+
static parse(filterString: string):OINODbSqlFilter {
|
|
67
|
+
// OINOLog_debug("OINOFilter.constructor", {filterString:filterString})
|
|
68
|
+
if (!filterString) {
|
|
69
|
+
return new OINODbSqlFilter("", null, "")
|
|
70
|
+
|
|
71
|
+
} else {
|
|
72
|
+
let match = OINODbSqlFilter._filterComparisonRegex.exec(filterString)
|
|
73
|
+
if (match != null) {
|
|
74
|
+
return new OINODbSqlFilter(match[1], match[2].toLowerCase() as OINODbSqlComparison, match[3])
|
|
75
|
+
} else {
|
|
76
|
+
let match = OINODbSqlFilter._negationRegex.exec(filterString)
|
|
77
|
+
if (match != null) {
|
|
78
|
+
return new OINODbSqlFilter("", OINODbSqlBooleanOperation.not, OINODbSqlFilter.parse(match[3]))
|
|
79
|
+
} else {
|
|
80
|
+
let boolean_parts = OINOStr.splitByBrackets(filterString, true, false, '(', ')')
|
|
81
|
+
// OINOLog_debug("OINOFilter.constructor", {boolean_parts:boolean_parts})
|
|
82
|
+
if (boolean_parts.length == 3 && (boolean_parts[1].match(OINODbSqlFilter._booleanOperationRegex))) {
|
|
83
|
+
return new OINODbSqlFilter(OINODbSqlFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1) as OINODbSqlBooleanOperation, OINODbSqlFilter.parse(boolean_parts[2]))
|
|
84
|
+
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error(OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Construct a new `OINOFilter` as combination of (boolean and/or) of two filters.
|
|
95
|
+
*
|
|
96
|
+
* @param leftSide left side to combine
|
|
97
|
+
* @param operation boolean operation to use in combination
|
|
98
|
+
* @param rightSide right side to combine
|
|
99
|
+
*
|
|
100
|
+
*/
|
|
101
|
+
static combine(leftSide:OINODbSqlFilter|undefined, operation:OINODbSqlBooleanOperation, rightSide:OINODbSqlFilter|undefined):OINODbSqlFilter|undefined {
|
|
102
|
+
if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
|
|
103
|
+
return new OINODbSqlFilter(leftSide, operation, rightSide)
|
|
104
|
+
|
|
105
|
+
} else if ((leftSide) && (!leftSide.isEmpty())) {
|
|
106
|
+
return leftSide
|
|
107
|
+
|
|
108
|
+
} else if ((rightSide) && (!rightSide.isEmpty())) {
|
|
109
|
+
return rightSide
|
|
110
|
+
|
|
111
|
+
} else {
|
|
112
|
+
return undefined
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
private _operatorToSql():string {
|
|
118
|
+
switch (this._operator) {
|
|
119
|
+
case "and": return " AND "
|
|
120
|
+
case "or": return " OR "
|
|
121
|
+
case "not": return "NOT "
|
|
122
|
+
case "lt": return " < "
|
|
123
|
+
case "le": return " <= "
|
|
124
|
+
case "eq": return " = "
|
|
125
|
+
case "ge": return " >= "
|
|
126
|
+
case "gt": return " > "
|
|
127
|
+
case "like": return " LIKE "
|
|
128
|
+
}
|
|
129
|
+
return " "
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Does filter contain any valid conditions.
|
|
134
|
+
*
|
|
135
|
+
*/
|
|
136
|
+
isEmpty():boolean {
|
|
137
|
+
return (this._leftSide == "") && (this._operator == null) && (this._rightSide == "")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Print filter as SQL condition based on the datamodel of the API.
|
|
142
|
+
*
|
|
143
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
144
|
+
*
|
|
145
|
+
*/
|
|
146
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
147
|
+
// OINOLog.debug("OINOFilter.toSql", {_leftSide:this._leftSide, _operator:this._operator, _rightSide:this._rightSide})
|
|
148
|
+
if (this.isEmpty()) {
|
|
149
|
+
return ""
|
|
150
|
+
}
|
|
151
|
+
let result:string = ""
|
|
152
|
+
let field:OINODbDataField|null = null
|
|
153
|
+
if (this._leftSide instanceof OINODbSqlFilter) {
|
|
154
|
+
result += this._leftSide.toSql(dataModel)
|
|
155
|
+
} else {
|
|
156
|
+
result += dataModel.api.db.printSqlColumnname(this._leftSide)
|
|
157
|
+
field = dataModel.findFieldByName(this._leftSide)
|
|
158
|
+
}
|
|
159
|
+
result += this._operatorToSql()
|
|
160
|
+
if (this._rightSide instanceof OINODbSqlFilter) {
|
|
161
|
+
result += this._rightSide.toSql(dataModel)
|
|
162
|
+
} else {
|
|
163
|
+
if (field) {
|
|
164
|
+
result += field.printCellAsSqlValue(this._rightSide)
|
|
165
|
+
} else {
|
|
166
|
+
result += this._rightSide
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
OINOLog.debug("OINOFilter.toSql", {result:result})
|
|
170
|
+
return "(" + result + ")"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Class for ordering select results on a number of columns.
|
|
176
|
+
*
|
|
177
|
+
*/
|
|
178
|
+
export class OINODbSqlOrder {
|
|
179
|
+
private static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC)?\s*?$/i
|
|
180
|
+
|
|
181
|
+
private _columns: string []
|
|
182
|
+
private _directions: string []
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Constructor for `OINODbSqlOrder`.
|
|
186
|
+
*
|
|
187
|
+
* @param orderString string representation of filter from HTTP-request
|
|
188
|
+
*
|
|
189
|
+
*/
|
|
190
|
+
constructor(orderString: string) {
|
|
191
|
+
// OINOLog.debug("OINODbSqlOrder.constructor", {orderString:orderString})
|
|
192
|
+
this._columns = []
|
|
193
|
+
this._directions = []
|
|
194
|
+
|
|
195
|
+
const column_strings = orderString.split(',')
|
|
196
|
+
for (let i=0; i<column_strings.length; i++) {
|
|
197
|
+
let match = OINODbSqlOrder._orderColumnRegex.exec(column_strings[i])
|
|
198
|
+
if (match != null) {
|
|
199
|
+
this._columns.push(match[1])
|
|
200
|
+
this._directions.push((match[2] || "ASC").toUpperCase())
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// OINOLog.debug("OINODbSqlOrder.constructor", {columns:this._columns, directions:this._directions})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Does filter contain any valid conditions.
|
|
208
|
+
*
|
|
209
|
+
*/
|
|
210
|
+
isEmpty():boolean {
|
|
211
|
+
return (this._columns.length == 0)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Print order as SQL condition based on the datamodel of the API.
|
|
216
|
+
*
|
|
217
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
218
|
+
*
|
|
219
|
+
*/
|
|
220
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
221
|
+
if (this.isEmpty()) {
|
|
222
|
+
return ""
|
|
223
|
+
}
|
|
224
|
+
// OINOLog.debug("OINODbSqlOrder.toSql", {columns:this._columns, directions:this._directions})
|
|
225
|
+
let result:string = ""
|
|
226
|
+
for (let i=0; i<this._columns.length; i++) {
|
|
227
|
+
const field:OINODbDataField|null = dataModel.findFieldByName(this._columns[i])
|
|
228
|
+
if (field) {
|
|
229
|
+
if (result) {
|
|
230
|
+
result += ","
|
|
231
|
+
}
|
|
232
|
+
result += dataModel.api.db.printSqlColumnname(field.name) + " " + this._directions[i]
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
|
|
236
|
+
return result
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Class for limiting the number of results.
|
|
242
|
+
*
|
|
243
|
+
*/
|
|
244
|
+
export class OINODbSqlLimit {
|
|
245
|
+
private static _orderColumnRegex = /^(\d+)?$/i
|
|
246
|
+
|
|
247
|
+
private _limit: number
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Constructor for `OINODbSqlLimit`.
|
|
251
|
+
*
|
|
252
|
+
* @param limitString string representation of filter from HTTP-request
|
|
253
|
+
*
|
|
254
|
+
*/
|
|
255
|
+
constructor(limitString: string) {
|
|
256
|
+
this._limit = 0
|
|
257
|
+
this._limit = Number.parseInt(limitString)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Does filter contain any valid conditions.
|
|
262
|
+
*
|
|
263
|
+
*/
|
|
264
|
+
isEmpty():boolean {
|
|
265
|
+
return (this._limit <= 0)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Print order as SQL condition based on the datamodel of the API.
|
|
270
|
+
*
|
|
271
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
272
|
+
*
|
|
273
|
+
*/
|
|
274
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
275
|
+
if (this.isEmpty()) {
|
|
276
|
+
return ""
|
|
277
|
+
}
|
|
278
|
+
let result:string = this._limit.toString()
|
|
279
|
+
return result
|
|
280
|
+
}
|
|
281
|
+
}
|