@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,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
+ }