@opra/core 0.0.12 → 0.1.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 (117) hide show
  1. package/cjs/enums/http-headers.enum.js +2 -1
  2. package/cjs/exception/http-errors/not-acceptable.error.js +26 -0
  3. package/cjs/exception/index.js +2 -0
  4. package/cjs/exception/resource-errors/resource-not-found.error.js +19 -0
  5. package/cjs/implementation/adapter-utils/entity-resource-execute.util.js +84 -0
  6. package/cjs/implementation/adapter-utils/resource-execute.util.js +11 -0
  7. package/cjs/implementation/adapter-utils/resource-prepare.util.js +11 -0
  8. package/cjs/implementation/{adapter/adapter.js → adapter.js} +40 -7
  9. package/cjs/implementation/{adapter/express-adapter.js → express-adapter.js} +0 -0
  10. package/cjs/implementation/http-adapter.js +267 -0
  11. package/cjs/implementation/query-context.js +5 -3
  12. package/cjs/index.js +5 -15
  13. package/cjs/{services/entity-resource-controller.js → interfaces/entity-service.interface.js} +0 -0
  14. package/cjs/interfaces/query.interface.js +26 -9
  15. package/cjs/services/json-data-service.js +241 -4
  16. package/cjs/utils/create-i18n.js +1 -1
  17. package/cjs/utils/get-caller-file.util.js +6 -1
  18. package/cjs/utils/{string-path-to-object-tree.js → path-to-tree.js} +3 -3
  19. package/esm/enums/http-headers.enum.d.ts +2 -1
  20. package/esm/enums/http-headers.enum.js +2 -1
  21. package/esm/exception/http-errors/not-acceptable.error.d.ts +10 -0
  22. package/esm/exception/http-errors/not-acceptable.error.js +22 -0
  23. package/esm/exception/index.d.ts +2 -0
  24. package/esm/exception/index.js +2 -0
  25. package/esm/exception/resource-errors/resource-not-found.error.d.ts +4 -0
  26. package/esm/exception/resource-errors/resource-not-found.error.js +15 -0
  27. package/esm/implementation/adapter-utils/entity-resource-execute.util.d.ts +3 -0
  28. package/esm/implementation/adapter-utils/entity-resource-execute.util.js +80 -0
  29. package/esm/implementation/adapter-utils/resource-execute.util.d.ts +3 -0
  30. package/esm/implementation/adapter-utils/resource-execute.util.js +7 -0
  31. package/esm/implementation/adapter-utils/resource-prepare.util.d.ts +3 -0
  32. package/esm/implementation/adapter-utils/resource-prepare.util.js +7 -0
  33. package/esm/implementation/{adapter/adapter.d.ts → adapter.d.ts} +5 -9
  34. package/esm/implementation/{adapter/adapter.js → adapter.js} +39 -6
  35. package/esm/implementation/{adapter/express-adapter.d.ts → express-adapter.d.ts} +2 -2
  36. package/esm/implementation/{adapter/express-adapter.js → express-adapter.js} +0 -0
  37. package/esm/implementation/{adapter/http-adapter.d.ts → http-adapter.d.ts} +6 -6
  38. package/esm/implementation/http-adapter.js +263 -0
  39. package/esm/implementation/query-context.d.ts +3 -4
  40. package/esm/implementation/query-context.js +5 -3
  41. package/esm/index.d.ts +5 -15
  42. package/esm/index.js +5 -15
  43. package/esm/{services/entity-resource-controller.d.ts → interfaces/entity-service.interface.d.ts} +0 -0
  44. package/esm/{services/entity-resource-controller.js → interfaces/entity-service.interface.js} +0 -0
  45. package/esm/interfaces/query.interface.d.ts +31 -25
  46. package/esm/interfaces/query.interface.js +26 -9
  47. package/esm/services/json-data-service.d.ts +66 -7
  48. package/esm/services/json-data-service.js +241 -4
  49. package/esm/types.d.ts +10 -8
  50. package/esm/utils/create-i18n.d.ts +1 -1
  51. package/esm/utils/create-i18n.js +1 -1
  52. package/esm/utils/get-caller-file.util.d.ts +1 -1
  53. package/esm/utils/get-caller-file.util.js +6 -1
  54. package/esm/utils/path-to-tree.d.ts +4 -0
  55. package/esm/utils/{string-path-to-object-tree.js → path-to-tree.js} +1 -1
  56. package/i18n/en/error.json +3 -0
  57. package/package.json +6 -7
  58. package/cjs/constants.js +0 -5
  59. package/cjs/decorators/entity-resource.decorator.js +0 -24
  60. package/cjs/implementation/adapter/http-adapter.js +0 -238
  61. package/cjs/implementation/data-type/complex-type.js +0 -39
  62. package/cjs/implementation/data-type/data-type.js +0 -35
  63. package/cjs/implementation/data-type/entity-type.js +0 -33
  64. package/cjs/implementation/data-type/simple-type.js +0 -30
  65. package/cjs/implementation/opra-document.js +0 -116
  66. package/cjs/implementation/opra-service.js +0 -59
  67. package/cjs/implementation/resource/container-resource-handler.js +0 -30
  68. package/cjs/implementation/resource/entity-resource-handler.js +0 -80
  69. package/cjs/implementation/resource/resource-handler.js +0 -31
  70. package/cjs/implementation/schema-generator.js +0 -163
  71. package/cjs/interfaces/opra-schema.metadata.js +0 -2
  72. package/cjs/interfaces/resource-container.interface.js +0 -2
  73. package/cjs/utils/class-utils.js +0 -37
  74. package/cjs/utils/headers.js +0 -58
  75. package/cjs/utils/internal-data-types.js +0 -81
  76. package/cjs/utils/responsive-object.js +0 -49
  77. package/cjs/utils/terminal-utils.js +0 -7
  78. package/esm/constants.d.ts +0 -2
  79. package/esm/constants.js +0 -2
  80. package/esm/decorators/entity-resource.decorator.d.ts +0 -5
  81. package/esm/decorators/entity-resource.decorator.js +0 -19
  82. package/esm/implementation/adapter/http-adapter.js +0 -234
  83. package/esm/implementation/data-type/complex-type.d.ts +0 -18
  84. package/esm/implementation/data-type/complex-type.js +0 -35
  85. package/esm/implementation/data-type/data-type.d.ts +0 -15
  86. package/esm/implementation/data-type/data-type.js +0 -31
  87. package/esm/implementation/data-type/entity-type.d.ts +0 -10
  88. package/esm/implementation/data-type/entity-type.js +0 -29
  89. package/esm/implementation/data-type/simple-type.d.ts +0 -15
  90. package/esm/implementation/data-type/simple-type.js +0 -26
  91. package/esm/implementation/opra-document.d.ts +0 -26
  92. package/esm/implementation/opra-document.js +0 -111
  93. package/esm/implementation/opra-service.d.ts +0 -19
  94. package/esm/implementation/opra-service.js +0 -55
  95. package/esm/implementation/resource/container-resource-handler.d.ts +0 -14
  96. package/esm/implementation/resource/container-resource-handler.js +0 -26
  97. package/esm/implementation/resource/entity-resource-handler.d.ts +0 -18
  98. package/esm/implementation/resource/entity-resource-handler.js +0 -75
  99. package/esm/implementation/resource/resource-handler.d.ts +0 -15
  100. package/esm/implementation/resource/resource-handler.js +0 -27
  101. package/esm/implementation/schema-generator.d.ts +0 -21
  102. package/esm/implementation/schema-generator.js +0 -159
  103. package/esm/interfaces/opra-schema.metadata.d.ts +0 -14
  104. package/esm/interfaces/opra-schema.metadata.js +0 -1
  105. package/esm/interfaces/resource-container.interface.d.ts +0 -6
  106. package/esm/interfaces/resource-container.interface.js +0 -1
  107. package/esm/utils/class-utils.d.ts +0 -6
  108. package/esm/utils/class-utils.js +0 -30
  109. package/esm/utils/headers.d.ts +0 -9
  110. package/esm/utils/headers.js +0 -55
  111. package/esm/utils/internal-data-types.d.ts +0 -5
  112. package/esm/utils/internal-data-types.js +0 -78
  113. package/esm/utils/responsive-object.d.ts +0 -3
  114. package/esm/utils/responsive-object.js +0 -45
  115. package/esm/utils/string-path-to-object-tree.d.ts +0 -4
  116. package/esm/utils/terminal-utils.d.ts +0 -4
  117. package/esm/utils/terminal-utils.js +0 -4
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpraQuery = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
- const complex_type_js_1 = require("../implementation/data-type/complex-type.js");
7
- const string_path_to_object_tree_js_1 = require("../utils/string-path-to-object-tree.js");
6
+ const schema_1 = require("@opra/schema");
7
+ const path_to_tree_js_1 = require("../utils/path-to-tree.js");
8
8
  var OpraQuery;
9
9
  (function (OpraQuery) {
10
10
  function forCreate(resource, values, options) {
@@ -25,7 +25,24 @@ var OpraQuery;
25
25
  return out;
26
26
  }
27
27
  OpraQuery.forCreate = forCreate;
28
- function forGet(resource, key, options) {
28
+ function forGetSchema(resourcePath, options) {
29
+ // if (options?.pick)
30
+ // options.pick = normalizePick(resource, options.pick);
31
+ // if (options?.omit)
32
+ // options.omit = normalizePick(resource, options.omit);
33
+ // if (options?.include)
34
+ // options.include = normalizePick(resource, options.include);
35
+ const out = {
36
+ queryType: 'schema',
37
+ scope: 'instance',
38
+ resourcePath,
39
+ operation: 'read',
40
+ };
41
+ Object.assign(out, lodash_1.default.omit(options, Object.keys(out)));
42
+ return out;
43
+ }
44
+ OpraQuery.forGetSchema = forGetSchema;
45
+ function forGetEntity(resource, key, options) {
29
46
  if (options?.pick)
30
47
  options.pick = normalizePick(resource, options.pick);
31
48
  if (options?.omit)
@@ -43,7 +60,7 @@ var OpraQuery;
43
60
  Object.assign(out, lodash_1.default.omit(options, Object.keys(out)));
44
61
  return out;
45
62
  }
46
- OpraQuery.forGet = forGet;
63
+ OpraQuery.forGetEntity = forGetEntity;
47
64
  function forSearch(resource, options) {
48
65
  if (options?.pick)
49
66
  options.pick = normalizePick(resource, options.pick);
@@ -151,19 +168,19 @@ function checkKeyFields(resource, key) {
151
168
  throw new Error(`"${resource.name}" has no primary key`);
152
169
  }
153
170
  function normalizePick(resource, fields) {
154
- const fieldsTree = (0, string_path_to_object_tree_js_1.stringPathToObjectTree)(fields) || {};
171
+ const fieldsTree = (0, path_to_tree_js_1.pathToTree)(fields) || {};
155
172
  return _normalizeFieldsList([], resource.service, resource, resource.dataType, fieldsTree, '', {
156
- additionalProperties: true
173
+ additionalFields: true
157
174
  });
158
175
  }
159
176
  function _normalizeFieldsList(target, service, resource, dataType, node, parentPath = '', options) {
160
177
  let curPath = '';
161
178
  for (const k of Object.keys(node)) {
162
179
  const nodeVal = node[k];
163
- const prop = dataType?.properties?.[k];
180
+ const prop = dataType?.fields.get(k);
164
181
  if (!prop) {
165
182
  curPath = parentPath ? parentPath + '.' + k : k;
166
- if (!options?.additionalProperties || (dataType && !dataType.additionalProperties))
183
+ if (!options?.additionalFields || (dataType && !dataType.additionalFields))
167
184
  throw new TypeError(`Unknown field path "${resource.name + '.' + curPath}"`);
168
185
  if (typeof nodeVal === 'object')
169
186
  _normalizeFieldsList(target, service, resource, undefined, nodeVal, curPath, options);
@@ -174,7 +191,7 @@ function _normalizeFieldsList(target, service, resource, dataType, node, parentP
174
191
  curPath = parentPath ? parentPath + '.' + prop.name : prop.name;
175
192
  const propType = service.getDataType(prop.type || 'string');
176
193
  if (typeof nodeVal === 'object') {
177
- if (!(propType instanceof complex_type_js_1.ComplexType))
194
+ if (!(propType && propType instanceof schema_1.ComplexType))
178
195
  throw new TypeError(`"${curPath}" is not a complex type and has no sub fields`);
179
196
  if (target.findIndex(x => x === parentPath) >= 0)
180
197
  continue;
@@ -2,14 +2,251 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.JsonDataService = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
5
6
  const rule_judgment_1 = tslib_1.__importDefault(require("rule-judgment"));
6
- const data_service_js_1 = require("./data-service.js");
7
+ const url_1 = require("@opra/url");
8
+ const index_js_1 = require("../exception/index.js");
7
9
  // Fix invalid importing (with ESM) of rule-judgment package
8
- const toArrayFilter = typeof rule_judgment_1.default === 'function' ? rule_judgment_1.default : rule_judgment_1.default.default;
9
- class JsonDataService extends data_service_js_1.DataService {
10
+ const createFilterFn = typeof rule_judgment_1.default === 'function'
11
+ ? rule_judgment_1.default
12
+ : rule_judgment_1.default.default;
13
+ class JsonDataService {
14
+ resourceName;
10
15
  data;
16
+ primaryKey;
11
17
  constructor(args) {
12
- super();
18
+ this.resourceName = args.resourceName;
19
+ this.data = args.data;
20
+ this.primaryKey = args.primaryKey;
21
+ }
22
+ processRequest(ctx) {
23
+ const prepared = JsonDataService.prepare(ctx.query);
24
+ const fn = this[prepared.method];
25
+ if (!fn)
26
+ throw new TypeError(`Unimplemented method (${prepared.method})`);
27
+ return fn.apply(this, prepared.args);
28
+ }
29
+ get(keyValue, options) {
30
+ const primaryKey = this.primaryKey;
31
+ let v = this.data.find(x => '' + x[primaryKey] === '' + keyValue);
32
+ v = JsonDataService.filterFields(v, options?.pick, options?.omit, options?.include);
33
+ return v;
34
+ }
35
+ count(options) {
36
+ return this.search(options).length;
37
+ }
38
+ search(options) {
39
+ let out;
40
+ if (options?.filter) {
41
+ const filter = JsonDataService.convertFilter(options.filter);
42
+ const filterFn = createFilterFn(filter);
43
+ out = this.data.filter(filterFn);
44
+ }
45
+ else
46
+ out = this.data;
47
+ return out.map(v => JsonDataService.filterFields(v, options?.pick, options?.omit, options?.include));
48
+ }
49
+ create(data, options) {
50
+ if (this.get(data[this.primaryKey]))
51
+ throw new index_js_1.ResourceConflictError(this.resourceName, this.primaryKey);
52
+ this.data.push(data);
53
+ return JsonDataService.filterFields(data, options?.pick, options?.omit, options?.include);
54
+ }
55
+ update(keyValue, data, options) {
56
+ const primaryKey = this.primaryKey;
57
+ const i = this.data.findIndex(x => '' + x[primaryKey] === '' + keyValue);
58
+ if (i >= 0) {
59
+ data = Object.assign(this.data[i], data);
60
+ return JsonDataService.filterFields(data, options?.pick, options?.omit, options?.include);
61
+ }
62
+ }
63
+ updateMany(data, options) {
64
+ const items = this.search({ filter: options?.filter });
65
+ for (let i = 0; i < items.length; i++) {
66
+ Object.assign(items[i], data);
67
+ }
68
+ return items.length;
69
+ }
70
+ delete(keyValue) {
71
+ const primaryKey = this.primaryKey;
72
+ const i = this.data.findIndex(x => '' + x[primaryKey] === '' + keyValue);
73
+ if (i >= 0) {
74
+ this.data = this.data.slice(i, 1);
75
+ return true;
76
+ }
77
+ return false;
78
+ }
79
+ deleteMany(options) {
80
+ const items = this.search({ filter: options?.filter });
81
+ this.data = this.data.filter(x => !items.includes(x));
82
+ return items.length;
83
+ }
84
+ static filterFields(obj,
85
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
86
+ pick,
87
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
88
+ omit,
89
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
+ include) {
91
+ if (!obj)
92
+ return;
93
+ return obj;
94
+ }
95
+ static prepare(query) {
96
+ switch (query.queryType) {
97
+ case 'create': {
98
+ const options = lodash_1.default.omitBy({
99
+ pick: query.pick?.length ? query.pick : undefined,
100
+ omit: query.omit?.length ? query.omit : undefined,
101
+ include: query.include?.length ? query.include : undefined,
102
+ }, lodash_1.default.isNil);
103
+ const { data } = query;
104
+ return {
105
+ method: query.queryType,
106
+ values: data,
107
+ options,
108
+ args: [data, options]
109
+ };
110
+ }
111
+ case 'get': {
112
+ const options = lodash_1.default.omitBy({
113
+ pick: query.pick?.length ? query.pick : undefined,
114
+ omit: query.omit?.length ? query.omit : undefined,
115
+ include: query.include?.length ? query.include : undefined,
116
+ }, lodash_1.default.isNil);
117
+ const keyValue = query.keyValue;
118
+ return {
119
+ method: query.queryType,
120
+ keyValue,
121
+ options,
122
+ args: [keyValue, options]
123
+ };
124
+ }
125
+ case 'search': {
126
+ const options = lodash_1.default.omitBy({
127
+ pick: query.pick?.length ? query.pick : undefined,
128
+ omit: query.omit?.length ? query.omit : undefined,
129
+ include: query.include?.length ? query.include : undefined,
130
+ sort: query.sort?.length ? query.sort : undefined,
131
+ limit: query.limit,
132
+ offset: query.skip,
133
+ distinct: query.distinct,
134
+ total: query.count,
135
+ filter: JsonDataService.convertFilter(query.filter)
136
+ }, lodash_1.default.isNil);
137
+ return {
138
+ method: query.queryType,
139
+ options,
140
+ args: [options]
141
+ };
142
+ }
143
+ case 'update': {
144
+ const options = lodash_1.default.omitBy({
145
+ pick: query.pick?.length ? query.pick : undefined,
146
+ omit: query.omit?.length ? query.omit : undefined,
147
+ include: query.include?.length ? query.include : undefined,
148
+ }, lodash_1.default.isNil);
149
+ const { data } = query;
150
+ const keyValue = query.keyValue;
151
+ return {
152
+ method: query.queryType,
153
+ keyValue: query.keyValue,
154
+ values: data,
155
+ options,
156
+ args: [keyValue, data, options]
157
+ };
158
+ }
159
+ case 'updateMany': {
160
+ const options = lodash_1.default.omitBy({
161
+ filter: JsonDataService.convertFilter(query.filter)
162
+ }, lodash_1.default.isNil);
163
+ const { data } = query;
164
+ return {
165
+ method: query.queryType,
166
+ options,
167
+ args: [data, options]
168
+ };
169
+ }
170
+ case 'delete': {
171
+ const options = {};
172
+ const keyValue = query.keyValue;
173
+ return {
174
+ method: query.queryType,
175
+ keyValue,
176
+ options,
177
+ args: [keyValue, options]
178
+ };
179
+ }
180
+ case 'deleteMany': {
181
+ const options = lodash_1.default.omitBy({
182
+ filter: JsonDataService.convertFilter(query.filter)
183
+ }, lodash_1.default.isNil);
184
+ return {
185
+ method: query.queryType,
186
+ options,
187
+ args: [options]
188
+ };
189
+ }
190
+ default:
191
+ throw new Error(`Unimplemented query type "${query.queryType}"`);
192
+ }
193
+ }
194
+ static convertFilter(str) {
195
+ const ast = typeof str === 'string'
196
+ ? (0, url_1.$parse)(str)
197
+ : str;
198
+ if (!ast || !(ast instanceof url_1.Expression))
199
+ return ast;
200
+ if (ast instanceof url_1.ComparisonExpression) {
201
+ const left = JsonDataService.convertFilter(ast.left);
202
+ const right = JsonDataService.convertFilter(ast.right);
203
+ switch (ast.op) {
204
+ case '=':
205
+ return { $eq: { [left]: right } };
206
+ case '!=':
207
+ return { $ne: { [left]: right } };
208
+ case '>':
209
+ return { $gt: { [left]: right } };
210
+ case '>=':
211
+ return { $gte: { [left]: right } };
212
+ case '<':
213
+ return { $lt: { [left]: right } };
214
+ case '<=':
215
+ return { $lte: { [left]: right } };
216
+ case 'in':
217
+ return { $in: { [left]: right } };
218
+ case '!in':
219
+ return { $nin: { [left]: right } };
220
+ default:
221
+ throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
222
+ }
223
+ }
224
+ if (ast instanceof url_1.QualifiedIdentifier) {
225
+ return ast.value;
226
+ }
227
+ if (ast instanceof url_1.NumberLiteral ||
228
+ ast instanceof url_1.StringLiteral ||
229
+ ast instanceof url_1.BooleanLiteral ||
230
+ ast instanceof url_1.NullLiteral ||
231
+ ast instanceof url_1.DateLiteral ||
232
+ ast instanceof url_1.TimeLiteral) {
233
+ return ast.value;
234
+ }
235
+ if (ast instanceof url_1.ArrayExpression) {
236
+ return ast.items.map(JsonDataService.convertFilter);
237
+ }
238
+ if (ast instanceof url_1.LogicalExpression) {
239
+ if (ast.op === 'or')
240
+ return { $or: ast.items.map(JsonDataService.convertFilter) };
241
+ return { $and: ast.items.map(JsonDataService.convertFilter) };
242
+ }
243
+ if (ast instanceof url_1.ArrayExpression) {
244
+ return ast.items.map(JsonDataService.convertFilter);
245
+ }
246
+ if (ast instanceof url_1.ParenthesesExpression) {
247
+ return JsonDataService.convertFilter(ast.expression);
248
+ }
249
+ throw new Error(`${ast.type} is not implemented yet`);
13
250
  }
14
251
  }
15
252
  exports.JsonDataService = JsonDataService;
@@ -8,8 +8,8 @@ const get_caller_file_util_js_1 = require("./get-caller-file.util.js");
8
8
  async function createI18n(options) {
9
9
  const opts = {
10
10
  ...options,
11
- resourceDirs: undefined
12
11
  };
12
+ delete opts.resourceDirs;
13
13
  const instance = i18n_1.I18n.createInstance(opts);
14
14
  await instance.init();
15
15
  await instance.loadResourceDir(path_1.default.resolve((0, get_caller_file_util_js_1.getCallerFile)(), '../../../i18n'));
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCallerFile = void 0;
4
+ const PATH_PATTERN = /^(?:file:\/\/)?(.+)$/;
4
5
  function getCallerFile(position = 1) {
5
6
  if (position >= Error.stackTraceLimit) {
6
7
  throw new TypeError('getCallerFile(position) requires position be less then Error.stackTraceLimit but position was: `' +
@@ -13,7 +14,11 @@ function getCallerFile(position = 1) {
13
14
  if (stack !== null && typeof stack === 'object') {
14
15
  // stack[0] holds this file
15
16
  // stack[1] holds where this function was called
16
- return stack[position] ? stack[position].getFileName() : undefined;
17
+ const s = stack[position] ?
18
+ stack[position].getFileName() : undefined;
19
+ const m = s ? PATH_PATTERN.exec(s) : undefined;
20
+ return m ? m[1] : '';
17
21
  }
22
+ return '';
18
23
  }
19
24
  exports.getCallerFile = getCallerFile;
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stringPathToObjectTree = void 0;
3
+ exports.pathToTree = void 0;
4
4
  const dotPattern = /^([^.]+)\.(.*)$/;
5
- function stringPathToObjectTree(arr) {
5
+ function pathToTree(arr) {
6
6
  if (!arr.length)
7
7
  return;
8
8
  return _stringPathToObjectTree(arr, {});
9
9
  }
10
- exports.stringPathToObjectTree = stringPathToObjectTree;
10
+ exports.pathToTree = pathToTree;
11
11
  function _stringPathToObjectTree(arr, target) {
12
12
  for (const k of arr) {
13
13
  const m = dotPattern.exec(k);
@@ -2,7 +2,8 @@
2
2
  * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#controls
3
3
  */
4
4
  export declare enum HttpHeaders {
5
- X_Opra_Version = "X-Opra-Version",
5
+ X_Opra_Version = "X-OPRA-Version",
6
+ X_Opra_Schema = "X-OPRA-Schema",
6
7
  /**
7
8
  * Defines the authentication method that should be used to access a resource.
8
9
  */
@@ -5,7 +5,8 @@
5
5
  export var HttpHeaders;
6
6
  (function (HttpHeaders) {
7
7
  /* *** Custom Headers *** */
8
- HttpHeaders["X_Opra_Version"] = "X-Opra-Version";
8
+ HttpHeaders["X_Opra_Version"] = "X-OPRA-Version";
9
+ HttpHeaders["X_Opra_Schema"] = "X-OPRA-Schema";
9
10
  /* *** Authentication *** */
10
11
  /**
11
12
  * Defines the authentication method that should be used to access a resource.
@@ -0,0 +1,10 @@
1
+ import { ApiException, ErrorResponse } from '../api-exception.js';
2
+ /**
3
+ * 406 Not Acceptable
4
+ * This response is sent when the web server, after performing server-driven content negotiation,
5
+ * doesn't find any content that conforms to the criteria given by the user agent.
6
+ */
7
+ export declare class NotAcceptableError extends ApiException {
8
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
9
+ protected _initResponse(response: Partial<ErrorResponse>): void;
10
+ }
@@ -0,0 +1,22 @@
1
+ import { translate } from '@opra/i18n';
2
+ import { HttpStatus } from '../../enums/index.js';
3
+ import { ApiException } from '../api-exception.js';
4
+ /**
5
+ * 406 Not Acceptable
6
+ * This response is sent when the web server, after performing server-driven content negotiation,
7
+ * doesn't find any content that conforms to the criteria given by the user agent.
8
+ */
9
+ export class NotAcceptableError extends ApiException {
10
+ constructor(response, cause) {
11
+ super(response, cause);
12
+ this.status = HttpStatus.NOT_ACCEPTABLE;
13
+ }
14
+ _initResponse(response) {
15
+ super._initResponse({
16
+ message: translate('error:NOT_ACCEPTABLE', 'Not Acceptable'),
17
+ severity: 'error',
18
+ code: 'NOT_ACCEPTABLE',
19
+ ...response
20
+ });
21
+ }
22
+ }
@@ -4,7 +4,9 @@ export * from './http-errors/failed-dependency.error.js';
4
4
  export * from './http-errors/forbidden.error.js';
5
5
  export * from './http-errors/internal-server.error.js';
6
6
  export * from './http-errors/method-not-allowed.error.js';
7
+ export * from './http-errors/not-acceptable.error.js';
7
8
  export * from './http-errors/not-found.error.js';
8
9
  export * from './http-errors/unauthorized.error.js';
9
10
  export * from './http-errors/unprocessable-entity.error.js';
10
11
  export * from './resource-errors/resource-conflict.error.js';
12
+ export * from './resource-errors/resource-not-found.error.js';
@@ -4,7 +4,9 @@ export * from './http-errors/failed-dependency.error.js';
4
4
  export * from './http-errors/forbidden.error.js';
5
5
  export * from './http-errors/internal-server.error.js';
6
6
  export * from './http-errors/method-not-allowed.error.js';
7
+ export * from './http-errors/not-acceptable.error.js';
7
8
  export * from './http-errors/not-found.error.js';
8
9
  export * from './http-errors/unauthorized.error.js';
9
10
  export * from './http-errors/unprocessable-entity.error.js';
10
11
  export * from './resource-errors/resource-conflict.error.js';
12
+ export * from './resource-errors/resource-not-found.error.js';
@@ -0,0 +1,4 @@
1
+ import { ApiException } from '../api-exception.js';
2
+ export declare class ResourceNotFoundError extends ApiException {
3
+ constructor(resource: string, keyValue: any, cause?: Error);
4
+ }
@@ -0,0 +1,15 @@
1
+ import { translate } from '@opra/i18n';
2
+ import { HttpStatus } from '../../enums/index.js';
3
+ import { ApiException } from '../api-exception.js';
4
+ export class ResourceNotFoundError extends ApiException {
5
+ constructor(resource, keyValue, cause) {
6
+ super({
7
+ message: translate(`error:RESOURCE_NOT_FOUND`, { resource: resource + '@' + keyValue }),
8
+ severity: 'error',
9
+ code: 'RESOURCE_NOT_FOUND',
10
+ subject: resource,
11
+ key: keyValue
12
+ }, cause);
13
+ this.status = HttpStatus.NOT_FOUND;
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ import { EntityResource, OpraService } from '@opra/schema';
2
+ import { QueryContext } from '../query-context.js';
3
+ export declare function entityResourceExecute(service: OpraService, resource: EntityResource, context: QueryContext): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import { translate } from '@opra/i18n';
2
+ import { ComplexType } from '@opra/schema';
3
+ import { HttpHeaders } from '../../enums/index.js';
4
+ import { ForbiddenError, ResourceNotFoundError } from '../../exception/index.js';
5
+ import { OpraQuery } from '../../interfaces/query.interface.js';
6
+ export async function entityResourceExecute(service, resource, context) {
7
+ const { query } = context;
8
+ if (OpraQuery.isSearchQuery(query)) {
9
+ const promises = [];
10
+ let search;
11
+ let count;
12
+ promises.push(executeFn(service, resource, context, query.queryType)
13
+ .then(v => search = v));
14
+ if (query.count) {
15
+ promises.push(executeFn(service, resource, context, 'count')
16
+ .then(v => count = v));
17
+ }
18
+ await Promise.all(promises);
19
+ context.response.value = {
20
+ ...search,
21
+ ...count
22
+ };
23
+ return;
24
+ }
25
+ context.response.value = await executeFn(service, resource, context, query.queryType);
26
+ }
27
+ async function executeFn(service, resource, context, queryType) {
28
+ const resolverInfo = resource.metadata.methods?.[queryType];
29
+ if (!resolverInfo.handler)
30
+ throw new ForbiddenError({
31
+ message: translate('RESOLVER_FORBIDDEN', { queryType }),
32
+ severity: 'error',
33
+ code: 'RESOLVER_FORBIDDEN'
34
+ });
35
+ let result = await resolverInfo.handler(context);
36
+ switch (queryType) {
37
+ case 'search':
38
+ context.response.headers.set(HttpHeaders.X_Opra_Schema, '/$schema/types/' + resource.dataType.name);
39
+ return {
40
+ items: Array.isArray(result) ? result : (context.response.value ? [result] : [])
41
+ };
42
+ case 'get':
43
+ case 'update':
44
+ if (!result) {
45
+ const query = context.query;
46
+ throw new ResourceNotFoundError(resource.name, query.keyValue);
47
+ }
48
+ break;
49
+ case 'count':
50
+ return { count: result || 0 };
51
+ case 'delete':
52
+ case 'deleteMany':
53
+ case 'updateMany':
54
+ let affectedRecords;
55
+ if (typeof result === 'number')
56
+ affectedRecords = result;
57
+ if (typeof result === 'boolean')
58
+ affectedRecords = result ? 1 : 0;
59
+ if (typeof result === 'object')
60
+ affectedRecords = result.affectedRows || result.affectedRecords;
61
+ return { affectedRecords };
62
+ }
63
+ if (!result)
64
+ return;
65
+ result = Array.isArray(result) ? result[0] : result;
66
+ let dataType = resource.dataType;
67
+ if (context.resultPath) {
68
+ const pathArray = context.resultPath.split('.');
69
+ for (const field of pathArray) {
70
+ const prop = dataType instanceof ComplexType ? dataType.fields.get(field) : undefined;
71
+ dataType = prop && prop.type ? service.types.get(prop.type) : undefined;
72
+ result = result && typeof result === 'object' && result[field];
73
+ }
74
+ }
75
+ if (queryType === 'create')
76
+ context.response.status = 201;
77
+ if (dataType)
78
+ context.response.headers.set(HttpHeaders.X_Opra_Schema, '/$schema/types/' + dataType.name);
79
+ return result;
80
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseResource, OpraService } from '@opra/schema';
2
+ import { QueryContext } from '../query-context.js';
3
+ export declare function resourceExecute(service: OpraService, resource: BaseResource, context: QueryContext): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { EntityResource } from '@opra/schema';
2
+ import { entityResourceExecute } from './entity-resource-execute.util.js';
3
+ export async function resourceExecute(service, resource, context) {
4
+ if (resource instanceof EntityResource)
5
+ return await entityResourceExecute(service, resource, context);
6
+ throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
7
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseResource } from '@opra/schema';
2
+ import { QueryContext } from '../query-context.js';
3
+ export declare function resourcePrepare(resource: BaseResource, context: QueryContext): Promise<void>;
@@ -0,0 +1,7 @@
1
+ export async function resourcePrepare(resource, context) {
2
+ const { query } = context;
3
+ const fn = resource.metadata['pre_' + query.queryType];
4
+ if (fn && typeof fn === 'function') {
5
+ await fn(context);
6
+ }
7
+ }
@@ -1,18 +1,13 @@
1
1
  import { FallbackLng, I18n, LanguageResource } from '@opra/i18n';
2
- import { ApiException } from '../../exception/index.js';
3
- import { IExecutionContext } from '../../interfaces/execution-context.interface.js';
4
- import { OpraService } from '../opra-service.js';
5
- import { QueryContext } from '../query-context.js';
2
+ import { OpraService } from '@opra/schema';
3
+ import { ApiException } from '../exception/index.js';
4
+ import { IExecutionContext } from '../interfaces/execution-context.interface.js';
5
+ import { QueryContext } from './query-context.js';
6
6
  export declare namespace OpraAdapter {
7
7
  type UserContextResolver = (args: {
8
8
  executionContext: IExecutionContext;
9
9
  isBatch: boolean;
10
10
  }) => object | Promise<object>;
11
- type RequestFinishEvent = (args: {
12
- executionContext: IExecutionContext;
13
- userContext?: any;
14
- failed: boolean;
15
- }) => Promise<void>;
16
11
  interface Options {
17
12
  i18n?: I18n | I18nOptions | (() => Promise<I18n>);
18
13
  userContext?: UserContextResolver;
@@ -57,5 +52,6 @@ export declare abstract class OpraAdapter<TExecutionContext extends IExecutionCo
57
52
  protected abstract sendError(executionContext: TExecutionContext, error: ApiException): Promise<void>;
58
53
  protected abstract isBatch(executionContext: TExecutionContext): boolean;
59
54
  protected handler(executionContext: TExecutionContext): Promise<void>;
55
+ protected _getSchemaExecute(ctx: QueryContext): Promise<void>;
60
56
  protected static initI18n(options?: OpraAdapter.Options): Promise<I18n>;
61
57
  }