@opra/core 0.1.1 → 0.2.0

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 (81) hide show
  1. package/cjs/enums/http-headers.enum.js +3 -2
  2. package/cjs/implementation/adapter-utils/entity-resource-execute.util.js +36 -34
  3. package/cjs/implementation/adapter-utils/resource-prepare.util.js +1 -1
  4. package/cjs/implementation/adapter.js +14 -19
  5. package/cjs/implementation/express-adapter.js +3 -0
  6. package/cjs/implementation/headers-map.js +18 -0
  7. package/cjs/implementation/http-adapter.js +65 -79
  8. package/cjs/implementation/query-context.js +12 -19
  9. package/cjs/index.js +0 -2
  10. package/cjs/services/json-data-service.js +367 -131
  11. package/cjs/utils/path-to-tree.js +7 -5
  12. package/esm/enums/http-headers.enum.d.ts +3 -2
  13. package/esm/enums/http-headers.enum.js +3 -2
  14. package/esm/implementation/adapter-utils/entity-resource-execute.util.js +35 -33
  15. package/esm/implementation/adapter-utils/resource-execute.util.d.ts +2 -2
  16. package/esm/implementation/adapter-utils/resource-prepare.util.d.ts +2 -2
  17. package/esm/implementation/adapter-utils/resource-prepare.util.js +1 -1
  18. package/esm/implementation/adapter.d.ts +2 -2
  19. package/esm/implementation/adapter.js +12 -17
  20. package/esm/implementation/express-adapter.js +2 -0
  21. package/esm/implementation/headers-map.d.ts +5 -0
  22. package/esm/implementation/headers-map.js +14 -0
  23. package/esm/implementation/http-adapter.d.ts +6 -6
  24. package/esm/implementation/http-adapter.js +61 -75
  25. package/esm/implementation/query-context.d.ts +9 -15
  26. package/esm/implementation/query-context.js +11 -17
  27. package/esm/index.d.ts +0 -2
  28. package/esm/index.js +0 -2
  29. package/esm/interfaces/entity-service.interface.d.ts +8 -6
  30. package/esm/services/json-data-service.d.ts +56 -39
  31. package/esm/services/json-data-service.js +365 -129
  32. package/esm/types.d.ts +0 -3
  33. package/esm/utils/path-to-tree.d.ts +1 -1
  34. package/esm/utils/path-to-tree.js +7 -5
  35. package/i18n/en/error.json +5 -5
  36. package/package.json +11 -8
  37. package/cjs/exception/api-exception.js +0 -68
  38. package/cjs/exception/http-errors/bad-request.error.js +0 -26
  39. package/cjs/exception/http-errors/failed-dependency.error.js +0 -25
  40. package/cjs/exception/http-errors/forbidden.error.js +0 -27
  41. package/cjs/exception/http-errors/internal-server.error.js +0 -27
  42. package/cjs/exception/http-errors/method-not-allowed.error.js +0 -26
  43. package/cjs/exception/http-errors/not-acceptable.error.js +0 -26
  44. package/cjs/exception/http-errors/not-found.error.js +0 -29
  45. package/cjs/exception/http-errors/unauthorized.error.js +0 -26
  46. package/cjs/exception/http-errors/unprocessable-entity.error.js +0 -25
  47. package/cjs/exception/index.js +0 -15
  48. package/cjs/exception/resource-errors/resource-conflict.error.js +0 -19
  49. package/cjs/exception/resource-errors/resource-not-found.error.js +0 -19
  50. package/cjs/exception/wrap-error.js +0 -17
  51. package/cjs/interfaces/query.interface.js +0 -207
  52. package/esm/exception/api-exception.d.ts +0 -40
  53. package/esm/exception/api-exception.js +0 -64
  54. package/esm/exception/http-errors/bad-request.error.d.ts +0 -10
  55. package/esm/exception/http-errors/bad-request.error.js +0 -22
  56. package/esm/exception/http-errors/failed-dependency.error.d.ts +0 -9
  57. package/esm/exception/http-errors/failed-dependency.error.js +0 -21
  58. package/esm/exception/http-errors/forbidden.error.d.ts +0 -11
  59. package/esm/exception/http-errors/forbidden.error.js +0 -23
  60. package/esm/exception/http-errors/internal-server.error.d.ts +0 -9
  61. package/esm/exception/http-errors/internal-server.error.js +0 -22
  62. package/esm/exception/http-errors/method-not-allowed.error.d.ts +0 -10
  63. package/esm/exception/http-errors/method-not-allowed.error.js +0 -22
  64. package/esm/exception/http-errors/not-acceptable.error.d.ts +0 -10
  65. package/esm/exception/http-errors/not-acceptable.error.js +0 -22
  66. package/esm/exception/http-errors/not-found.error.d.ts +0 -13
  67. package/esm/exception/http-errors/not-found.error.js +0 -25
  68. package/esm/exception/http-errors/unauthorized.error.d.ts +0 -10
  69. package/esm/exception/http-errors/unauthorized.error.js +0 -22
  70. package/esm/exception/http-errors/unprocessable-entity.error.d.ts +0 -9
  71. package/esm/exception/http-errors/unprocessable-entity.error.js +0 -21
  72. package/esm/exception/index.d.ts +0 -12
  73. package/esm/exception/index.js +0 -12
  74. package/esm/exception/resource-errors/resource-conflict.error.d.ts +0 -4
  75. package/esm/exception/resource-errors/resource-conflict.error.js +0 -15
  76. package/esm/exception/resource-errors/resource-not-found.error.d.ts +0 -4
  77. package/esm/exception/resource-errors/resource-not-found.error.js +0 -15
  78. package/esm/exception/wrap-error.d.ts +0 -2
  79. package/esm/exception/wrap-error.js +0 -13
  80. package/esm/interfaces/query.interface.d.ts +0 -115
  81. package/esm/interfaces/query.interface.js +0 -203
@@ -1,151 +1,311 @@
1
1
  import _ from 'lodash';
2
- import ruleJudgment from 'rule-judgment';
2
+ import merge from 'putil-merge';
3
+ import { nSQL } from "@nano-sql/core";
4
+ import { BadRequestError, MethodNotAllowedError, ResourceConflictError } from '@opra/exception';
5
+ import { ComplexType, EntityResource } from '@opra/schema';
3
6
  import { $parse, ArrayExpression, BooleanLiteral, ComparisonExpression, DateLiteral, Expression, LogicalExpression, NullLiteral, NumberLiteral, ParenthesesExpression, QualifiedIdentifier, StringLiteral, TimeLiteral } from '@opra/url';
4
- import { ResourceConflictError } from '../exception/index.js';
5
- // Fix invalid importing (with ESM) of rule-judgment package
6
- const createFilterFn = typeof ruleJudgment === 'function'
7
- ? ruleJudgment
8
- : ruleJudgment.default;
7
+ import { pathToTree } from '../utils/path-to-tree.js';
8
+ let dbId = 1;
9
9
  export class JsonDataService {
10
- resourceName;
11
- data;
12
- primaryKey;
13
- constructor(args) {
14
- this.resourceName = args.resourceName;
15
- this.data = args.data;
16
- this.primaryKey = args.primaryKey;
17
- }
18
- processRequest(ctx) {
19
- const prepared = JsonDataService.prepare(ctx.query);
10
+ resource;
11
+ _status = '';
12
+ _initError;
13
+ _dbName;
14
+ _initData;
15
+ defaultLimit;
16
+ constructor(resource, options) {
17
+ this.resource = resource;
18
+ this.defaultLimit = options?.defaultLimit ?? 10;
19
+ this._initData = options?.data;
20
+ }
21
+ get dataType() {
22
+ return this.resource.dataType;
23
+ }
24
+ get primaryKey() {
25
+ return this.resource.dataType.primaryKey;
26
+ }
27
+ get resourceName() {
28
+ return this.resource.name;
29
+ }
30
+ async close() {
31
+ await this._waitInitializing();
32
+ if (this._status === 'initialized') {
33
+ this._status = 'initializing';
34
+ try {
35
+ await nSQL().disconnect(this._dbName);
36
+ }
37
+ finally {
38
+ this._status = '';
39
+ }
40
+ }
41
+ }
42
+ async processRequest(ctx) {
43
+ const prepared = this._prepare(ctx.query);
20
44
  const fn = this[prepared.method];
21
45
  if (!fn)
22
46
  throw new TypeError(`Unimplemented method (${prepared.method})`);
47
+ // @ts-ignore
23
48
  return fn.apply(this, prepared.args);
24
49
  }
25
- get(keyValue, options) {
26
- const primaryKey = this.primaryKey;
27
- let v = this.data.find(x => '' + x[primaryKey] === '' + keyValue);
28
- v = JsonDataService.filterFields(v, options?.pick, options?.omit, options?.include);
29
- return v;
30
- }
31
- count(options) {
32
- return this.search(options).length;
33
- }
34
- search(options) {
35
- let out;
36
- if (options?.filter) {
37
- const filter = JsonDataService.convertFilter(options.filter);
38
- const filterFn = createFilterFn(filter);
39
- out = this.data.filter(filterFn);
40
- }
41
- else
42
- out = this.data;
43
- return out.map(v => JsonDataService.filterFields(v, options?.pick, options?.omit, options?.include));
50
+ async get(keyValue, options) {
51
+ await this._init();
52
+ const select = this._convertSelect({
53
+ pick: options?.pick,
54
+ omit: options?.omit,
55
+ include: options?.include,
56
+ });
57
+ nSQL().useDatabase(this._dbName);
58
+ const rows = await nSQL(this.resourceName)
59
+ .query('select', select)
60
+ .where([this.primaryKey, '=', keyValue])
61
+ .exec();
62
+ return unFlatten(rows[0]);
63
+ }
64
+ async count(options) {
65
+ await this._init();
66
+ nSQL().useDatabase(this._dbName);
67
+ const rows = await nSQL(this.resourceName)
68
+ .query('select', ['COUNT(*) as count'])
69
+ .where(options?.filter || [])
70
+ .exec();
71
+ return (rows[0]?.count) || 0;
72
+ }
73
+ async search(options) {
74
+ await this._init();
75
+ const select = this._convertSelect({
76
+ pick: options?.pick,
77
+ omit: options?.omit,
78
+ include: options?.include,
79
+ });
80
+ const filter = this._convertFilter(options?.filter);
81
+ nSQL().useDatabase(this._dbName);
82
+ const query = nSQL(this.resourceName)
83
+ .query('select', select)
84
+ .limit(options?.limit || 10)
85
+ .offset(options?.skip || 0)
86
+ .orderBy(options?.sort || [])
87
+ .where(filter || []);
88
+ return (await query.exec()).map(x => unFlatten(x));
44
89
  }
45
- create(data, options) {
46
- if (this.get(data[this.primaryKey]))
90
+ async create(data, options) {
91
+ if (!data[this.primaryKey])
92
+ throw new BadRequestError({
93
+ message: 'You must provide primary key value'
94
+ });
95
+ await this._init();
96
+ const keyValue = data[this.primaryKey];
97
+ nSQL().useDatabase(this._dbName);
98
+ const rows = await nSQL(this._initData).query('select', [this.primaryKey])
99
+ .where([this.primaryKey, '=', keyValue])
100
+ .exec();
101
+ if (rows.length)
47
102
  throw new ResourceConflictError(this.resourceName, this.primaryKey);
48
- this.data.push(data);
49
- return JsonDataService.filterFields(data, options?.pick, options?.omit, options?.include);
50
- }
51
- update(keyValue, data, options) {
52
- const primaryKey = this.primaryKey;
53
- const i = this.data.findIndex(x => '' + x[primaryKey] === '' + keyValue);
54
- if (i >= 0) {
55
- data = Object.assign(this.data[i], data);
56
- return JsonDataService.filterFields(data, options?.pick, options?.omit, options?.include);
103
+ await nSQL(this._initData).query('upsert', data)
104
+ .exec();
105
+ return await this.get(keyValue, options);
106
+ }
107
+ async update(keyValue, data, options) {
108
+ await this._init();
109
+ nSQL().useDatabase(this._dbName);
110
+ await nSQL(this._initData)
111
+ .query('conform rows', (row) => {
112
+ const out = merge({}, row, { deep: true, clone: true });
113
+ merge(out, data, { deep: true });
114
+ return out;
115
+ })
116
+ .where([this.primaryKey, '=', keyValue])
117
+ .exec();
118
+ await nSQL(this._initData).query("rebuild indexes").exec();
119
+ return this.get(keyValue, options);
120
+ }
121
+ async updateMany(data, options) {
122
+ await this._init();
123
+ const filter = this._convertFilter(options?.filter);
124
+ await this._init();
125
+ nSQL().useDatabase(this._dbName);
126
+ let affected = 0;
127
+ await nSQL(this._initData)
128
+ .query('conform rows', (row) => {
129
+ const out = merge({}, row, { deep: true, clone: true });
130
+ merge(out, data, { deep: true });
131
+ affected++;
132
+ return out;
133
+ })
134
+ .where(filter)
135
+ .exec();
136
+ await nSQL(this._initData).query("rebuild indexes").exec();
137
+ return affected;
138
+ }
139
+ async delete(keyValue) {
140
+ await this._init();
141
+ nSQL().useDatabase(this._dbName);
142
+ const result = await nSQL(this._initData)
143
+ .query('delete')
144
+ .where([this.primaryKey, '=', keyValue])
145
+ .exec();
146
+ return !!result.length;
147
+ }
148
+ async deleteMany(options) {
149
+ await this._init();
150
+ const filter = this._convertFilter(options?.filter);
151
+ nSQL().useDatabase(this._dbName);
152
+ const result = await nSQL(this._initData)
153
+ .query('delete')
154
+ .where(filter)
155
+ .exec();
156
+ return result.length;
157
+ }
158
+ async _waitInitializing() {
159
+ if (this._status === 'initializing') {
160
+ return new Promise((resolve, reject) => {
161
+ const reTry = () => setTimeout(() => {
162
+ if (this._status === '')
163
+ return resolve(this._init());
164
+ if (this._status === 'error')
165
+ return reject(this._initError);
166
+ if (this._status === 'initialized')
167
+ return resolve();
168
+ reTry();
169
+ }, 50).unref();
170
+ reTry();
171
+ });
57
172
  }
58
173
  }
59
- updateMany(data, options) {
60
- const items = this.search({ filter: options?.filter });
61
- for (let i = 0; i < items.length; i++) {
62
- Object.assign(items[i], data);
174
+ async _init() {
175
+ await this._waitInitializing();
176
+ if (this._status === 'initialized')
177
+ return;
178
+ this._status = 'initializing';
179
+ this._dbName = 'JsonDataService_DB_' + (dbId++);
180
+ try {
181
+ const model = {
182
+ name: this.resourceName,
183
+ model: {
184
+ '*:any': {}
185
+ },
186
+ indexes: {},
187
+ primaryKey: this.primaryKey
188
+ };
189
+ // Add indexes for sort fields
190
+ const searchMethod = this.resource.metadata.methods.search;
191
+ if (searchMethod) {
192
+ if (searchMethod.sortFields) {
193
+ searchMethod.sortFields.forEach(fieldName => {
194
+ const f = this.dataType.getField(fieldName);
195
+ const fieldType = this.resource.owner.getDataType(f.type || 'string');
196
+ model.indexes[fieldName + ':' + dataTypeToSQLType(fieldType, !!f.isArray)] = {};
197
+ });
198
+ }
199
+ if (searchMethod.filters) {
200
+ searchMethod.filters.forEach(filter => {
201
+ const f = this.dataType.getField(filter.field);
202
+ const fieldType = this.resource.owner.getDataType(f.type || 'string');
203
+ model.indexes[filter.field + ':' + dataTypeToSQLType(fieldType, !!f.isArray)] = {};
204
+ });
205
+ }
206
+ }
207
+ await nSQL().createDatabase({
208
+ id: this._dbName,
209
+ version: 3,
210
+ tables: [model]
211
+ });
212
+ this._status = 'initialized';
213
+ if (this._initData) {
214
+ nSQL().useDatabase(this._dbName);
215
+ await nSQL(this.resourceName)
216
+ .query('upsert', this._initData)
217
+ .exec();
218
+ delete this._initData;
219
+ }
63
220
  }
64
- return items.length;
65
- }
66
- delete(keyValue) {
67
- const primaryKey = this.primaryKey;
68
- const i = this.data.findIndex(x => '' + x[primaryKey] === '' + keyValue);
69
- if (i >= 0) {
70
- this.data = this.data.slice(i, 1);
71
- return true;
221
+ catch (e) {
222
+ this._initError = e;
223
+ this._status = 'error';
72
224
  }
73
- return false;
74
- }
75
- deleteMany(options) {
76
- const items = this.search({ filter: options?.filter });
77
- this.data = this.data.filter(x => !items.includes(x));
78
- return items.length;
79
- }
80
- static filterFields(obj,
81
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
82
- pick,
83
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
- omit,
85
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
86
- include) {
87
- if (!obj)
88
- return;
89
- return obj;
90
225
  }
91
- static prepare(query) {
92
- switch (query.queryType) {
93
- case 'create': {
226
+ _prepare(query) {
227
+ if (query.resource instanceof EntityResource) {
228
+ if (query.dataType !== this.dataType)
229
+ throw new TypeError(`Query data type (${query.dataType.name}) ` +
230
+ `differs from JsonDataService data type (${this.dataType.name})`);
231
+ }
232
+ switch (query.method) {
233
+ case 'count': {
94
234
  const options = _.omitBy({
95
- pick: query.pick?.length ? query.pick : undefined,
96
- omit: query.omit?.length ? query.omit : undefined,
97
- include: query.include?.length ? query.include : undefined,
235
+ filter: this._convertFilter(query.filter)
98
236
  }, _.isNil);
99
- const { data } = query;
100
237
  return {
101
- method: query.queryType,
102
- values: data,
238
+ method: query.method,
103
239
  options,
104
- args: [data, options]
240
+ args: [options]
105
241
  };
106
242
  }
107
- case 'get': {
243
+ case 'create': {
108
244
  const options = _.omitBy({
109
- pick: query.pick?.length ? query.pick : undefined,
110
- omit: query.omit?.length ? query.omit : undefined,
111
- include: query.include?.length ? query.include : undefined,
245
+ pick: query.pick,
246
+ omit: query.omit,
247
+ include: query.include
112
248
  }, _.isNil);
113
- const keyValue = query.keyValue;
249
+ const { data } = query;
114
250
  return {
115
- method: query.queryType,
116
- keyValue,
251
+ method: query.method,
252
+ values: data,
117
253
  options,
118
- args: [keyValue, options]
254
+ args: [data, options]
119
255
  };
120
256
  }
257
+ case 'get': {
258
+ if (query.kind === 'GetInstanceQuery') {
259
+ const options = _.omitBy({
260
+ pick: query.pick,
261
+ omit: query.omit,
262
+ include: query.include
263
+ }, _.isNil);
264
+ const keyValue = query.keyValue;
265
+ return {
266
+ method: query.method,
267
+ keyValue,
268
+ options,
269
+ args: [keyValue, options]
270
+ };
271
+ }
272
+ if (query.kind === 'GetFieldQuery') {
273
+ // todo
274
+ }
275
+ break;
276
+ }
121
277
  case 'search': {
278
+ if (query.distinct)
279
+ throw new MethodNotAllowedError({
280
+ message: '$distinct parameter is not supported by JsonDataService'
281
+ });
122
282
  const options = _.omitBy({
123
- pick: query.pick?.length ? query.pick : undefined,
124
- omit: query.omit?.length ? query.omit : undefined,
125
- include: query.include?.length ? query.include : undefined,
283
+ pick: query.pick,
284
+ omit: query.omit,
285
+ include: query.include,
286
+ filter: this._convertFilter(query.filter),
126
287
  sort: query.sort?.length ? query.sort : undefined,
288
+ skip: query.skip,
127
289
  limit: query.limit,
128
290
  offset: query.skip,
129
- distinct: query.distinct,
130
- total: query.count,
131
- filter: JsonDataService.convertFilter(query.filter)
291
+ count: query.count,
132
292
  }, _.isNil);
133
293
  return {
134
- method: query.queryType,
294
+ method: query.method,
135
295
  options,
136
296
  args: [options]
137
297
  };
138
298
  }
139
299
  case 'update': {
140
300
  const options = _.omitBy({
141
- pick: query.pick?.length ? query.pick : undefined,
142
- omit: query.omit?.length ? query.omit : undefined,
143
- include: query.include?.length ? query.include : undefined,
301
+ pick: query.pick,
302
+ omit: query.omit,
303
+ include: query.include
144
304
  }, _.isNil);
145
305
  const { data } = query;
146
306
  const keyValue = query.keyValue;
147
307
  return {
148
- method: query.queryType,
308
+ method: query.method,
149
309
  keyValue: query.keyValue,
150
310
  values: data,
151
311
  options,
@@ -154,11 +314,11 @@ export class JsonDataService {
154
314
  }
155
315
  case 'updateMany': {
156
316
  const options = _.omitBy({
157
- filter: JsonDataService.convertFilter(query.filter)
317
+ filter: this._convertFilter(query.filter)
158
318
  }, _.isNil);
159
319
  const { data } = query;
160
320
  return {
161
- method: query.queryType,
321
+ method: query.method,
162
322
  options,
163
323
  args: [data, options]
164
324
  };
@@ -167,7 +327,7 @@ export class JsonDataService {
167
327
  const options = {};
168
328
  const keyValue = query.keyValue;
169
329
  return {
170
- method: query.queryType,
330
+ method: query.method,
171
331
  keyValue,
172
332
  options,
173
333
  args: [keyValue, options]
@@ -175,44 +335,70 @@ export class JsonDataService {
175
335
  }
176
336
  case 'deleteMany': {
177
337
  const options = _.omitBy({
178
- filter: JsonDataService.convertFilter(query.filter)
338
+ filter: this._convertFilter(query.filter)
179
339
  }, _.isNil);
180
340
  return {
181
- method: query.queryType,
341
+ method: query.method,
182
342
  options,
183
343
  args: [options]
184
344
  };
185
345
  }
186
- default:
187
- throw new Error(`Unimplemented query type "${query.queryType}"`);
188
346
  }
347
+ throw new Error(`Unimplemented query type "${query.method}"`);
348
+ }
349
+ _convertSelect(args) {
350
+ const result = [];
351
+ const document = this.dataType.owner;
352
+ const processDataType = (dt, path, pick, omit, include) => {
353
+ let kl;
354
+ for (const [k, f] of dt.fields) {
355
+ kl = k.toLowerCase();
356
+ if (omit?.[kl] === true)
357
+ continue;
358
+ if ((((!pick && !f.exclusive) || pick?.[kl])) || include?.[kl]) {
359
+ const fieldType = document.getDataType(f.type);
360
+ const subPath = (path ? path + '.' : '') + f.name;
361
+ if (fieldType instanceof ComplexType) {
362
+ processDataType(fieldType, subPath, typeof pick?.[kl] === 'object' ? pick?.[kl] : undefined, typeof omit?.[kl] === 'object' ? omit?.[kl] : undefined, typeof include?.[kl] === 'object' ? include?.[kl] : undefined);
363
+ continue;
364
+ }
365
+ result.push(subPath);
366
+ }
367
+ }
368
+ };
369
+ processDataType(this.dataType, '', (args.pick ? pathToTree(args.pick, true) : undefined), (args.omit ? pathToTree(args.omit, true) : undefined), (args.include ? pathToTree(args.include, true) : undefined));
370
+ return result;
189
371
  }
190
- static convertFilter(str) {
372
+ _convertFilter(str) {
191
373
  const ast = typeof str === 'string'
192
374
  ? $parse(str)
193
375
  : str;
194
376
  if (!ast || !(ast instanceof Expression))
195
377
  return ast;
196
378
  if (ast instanceof ComparisonExpression) {
197
- const left = JsonDataService.convertFilter(ast.left);
198
- const right = JsonDataService.convertFilter(ast.right);
379
+ const left = this._convertFilter(ast.left);
380
+ const right = this._convertFilter(ast.right);
199
381
  switch (ast.op) {
200
382
  case '=':
201
- return { $eq: { [left]: right } };
383
+ return [left, '=', right];
202
384
  case '!=':
203
- return { $ne: { [left]: right } };
385
+ return [left, '!=', right];
204
386
  case '>':
205
- return { $gt: { [left]: right } };
387
+ return [left, '>', right];
206
388
  case '>=':
207
- return { $gte: { [left]: right } };
389
+ return [left, '>=', right];
208
390
  case '<':
209
- return { $lt: { [left]: right } };
391
+ return [left, '<', right];
210
392
  case '<=':
211
- return { $lte: { [left]: right } };
393
+ return [left, '<=', right];
394
+ case 'like':
395
+ return [left, 'LIKE', right];
396
+ case '!like':
397
+ return [left, 'NOT LIKE', right];
212
398
  case 'in':
213
- return { $in: { [left]: right } };
399
+ return [left, 'IN', Array.isArray(right) ? right : [right]];
214
400
  case '!in':
215
- return { $nin: { [left]: right } };
401
+ return [left, 'NOT IN', Array.isArray(right) ? right : [right]];
216
402
  default:
217
403
  throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
218
404
  }
@@ -229,19 +415,69 @@ export class JsonDataService {
229
415
  return ast.value;
230
416
  }
231
417
  if (ast instanceof ArrayExpression) {
232
- return ast.items.map(JsonDataService.convertFilter);
418
+ return ast.items.map(item => this._convertFilter(item));
233
419
  }
234
420
  if (ast instanceof LogicalExpression) {
235
- if (ast.op === 'or')
236
- return { $or: ast.items.map(JsonDataService.convertFilter) };
237
- return { $and: ast.items.map(JsonDataService.convertFilter) };
421
+ return ast.items.map(item => this._convertFilter(item))
422
+ .reduce((a, v) => {
423
+ if (a.length)
424
+ a.push(ast.op.toUpperCase());
425
+ a.push(v);
426
+ return a;
427
+ }, []);
238
428
  }
239
429
  if (ast instanceof ArrayExpression) {
240
- return ast.items.map(JsonDataService.convertFilter);
430
+ return ast.items.map(item => this._convertFilter(item));
241
431
  }
242
432
  if (ast instanceof ParenthesesExpression) {
243
- return JsonDataService.convertFilter(ast.expression);
433
+ return this._convertFilter(ast.expression);
434
+ }
435
+ throw new Error(`${ast.kind} is not implemented yet`);
436
+ }
437
+ }
438
+ function unFlatten(input) {
439
+ if (!input)
440
+ return;
441
+ const target = {};
442
+ for (const k of Object.keys(input)) {
443
+ if (k.includes('.')) {
444
+ const keys = k.split('.');
445
+ let o = target;
446
+ for (let i = 0; i < keys.length - 1; i++) {
447
+ o = o[keys[i]] = o[keys[i]] || {};
448
+ }
449
+ o[keys[keys.length - 1]] = input[k];
450
+ }
451
+ else
452
+ target[k] = input[k];
453
+ }
454
+ return target;
455
+ }
456
+ function dataTypeToSQLType(dataType, isArray) {
457
+ let out = 'any';
458
+ if (dataType.kind !== 'SimpleType')
459
+ out = 'object';
460
+ else {
461
+ switch (dataType.name) {
462
+ case 'booolean':
463
+ case 'number':
464
+ case 'string':
465
+ out = dataType.name;
466
+ break;
467
+ case 'integer':
468
+ out = 'int';
469
+ break;
470
+ case 'date':
471
+ case 'date-time':
472
+ out = 'date';
473
+ break;
474
+ case 'time':
475
+ out = 'string';
476
+ break;
477
+ case 'uuid':
478
+ out = 'uuid';
479
+ break;
244
480
  }
245
- throw new Error(`${ast.type} is not implemented yet`);
246
481
  }
482
+ return out + (isArray ? '[]' : '');
247
483
  }
package/esm/types.d.ts CHANGED
@@ -1,7 +1,4 @@
1
1
  import { Builtin, DeepPickWritable } from 'ts-gems';
2
- import { OpraSchema } from '@opra/schema';
3
- export declare type QueryType = OpraSchema.EntityMethodType | 'schema' | 'execute';
4
- export declare type KeyValue = string | number | boolean | object;
5
2
  export declare type EntityInput<T> = DeepNullableIfPartial<DeepPickWritable<T>>;
6
3
  export declare type EntityOutput<T> = DeepNullableIfPartial<T>;
7
4
  export declare type DeepNullableIfPartial<T> = _DeepNullableIfPartial<T>;
@@ -1,4 +1,4 @@
1
1
  export interface ObjectTree {
2
2
  [key: string]: boolean | ObjectTree;
3
3
  }
4
- export declare function pathToTree(arr: string[]): ObjectTree | undefined;
4
+ export declare function pathToTree(arr: string[], lowerCaseKeys?: boolean): ObjectTree | undefined;
@@ -1,18 +1,20 @@
1
1
  const dotPattern = /^([^.]+)\.(.*)$/;
2
- export function pathToTree(arr) {
2
+ export function pathToTree(arr, lowerCaseKeys) {
3
3
  if (!arr.length)
4
4
  return;
5
- return _stringPathToObjectTree(arr, {});
5
+ return _pathToTree(arr, {}, lowerCaseKeys);
6
6
  }
7
- function _stringPathToObjectTree(arr, target) {
8
- for (const k of arr) {
7
+ function _pathToTree(arr, target, lowerCaseKeys) {
8
+ for (let k of arr) {
9
+ if (lowerCaseKeys)
10
+ k = k.toLowerCase();
9
11
  const m = dotPattern.exec(k);
10
12
  if (m) {
11
13
  const key = m[1];
12
14
  if (target[key] === true)
13
15
  continue;
14
16
  const sub = target[key] = typeof target[key] === 'object' ? target[key] : {};
15
- _stringPathToObjectTree([m[2]], sub);
17
+ _pathToTree([m[2]], sub);
16
18
  }
17
19
  else {
18
20
  target[k] = true;
@@ -3,13 +3,13 @@
3
3
  "FAILED_DEPENDENCY": "The request failed due to failure of a previous request",
4
4
  "FORBIDDEN": "You are not authorized to perform this action",
5
5
  "INTERNAL_SERVER_ERROR": "Internal server error",
6
- "METHOD_NOT_ALLOWED": "Method Not Allowed",
7
- "NOT_ACCEPTABLE": "Not Acceptable",
6
+ "METHOD_NOT_ALLOWED": "Method not allowed",
7
+ "NOT_ACCEPTABLE": "Not acceptable",
8
8
  "NOT_FOUND": "Not found",
9
9
  "UNAUTHORIZED": "You have not been authenticated to perform this action",
10
10
  "UNPROCESSABLE_ENTITY": "Unprocessable entity",
11
-
12
- "RESOLVER_FORBIDDEN": "The resource endpoint does not accept '{{queryType}}' operations",
13
11
  "RESOURCE_NOT_FOUND": "The resource '{{resource}}' could not be found",
14
- "RESOURCE_CONFLICT": "There is already an other {{resource}} resource with same field values ({{fields}})"
12
+ "RESOURCE_CONFLICT": "There is already an other {{resource}} resource with same field values ({{fields}})",
13
+ "RESOLVER_FORBIDDEN": "The resource endpoint does not accept '{{method}}' operations",
14
+ "UNACCEPTED_SORT_FIELD": "Field '{{field}}' is not available for sort operation"
15
15
  }