@opra/common 0.22.0 → 0.23.1

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 (181) hide show
  1. package/browser.js +957 -1566
  2. package/cjs/document/api-document.js +7 -3
  3. package/cjs/document/data-type/api-field.js +2 -2
  4. package/cjs/document/data-type/complex-type.js +17 -63
  5. package/cjs/document/data-type/data-type.js +0 -9
  6. package/cjs/document/data-type/enum-type.js +2 -8
  7. package/cjs/document/data-type/mapped-type.js +0 -36
  8. package/cjs/document/data-type/simple-type.js +2 -8
  9. package/cjs/document/data-type/union-type.js +1 -37
  10. package/cjs/document/index.js +2 -0
  11. package/cjs/document/resource/collection.js +75 -32
  12. package/cjs/document/resource/singleton.js +31 -20
  13. package/cjs/document/resource/storage.js +6 -20
  14. package/cjs/document/utils/generate-codec.js +39 -0
  15. package/cjs/exception/http-errors/bad-request.error.js +2 -3
  16. package/cjs/exception/http-errors/failed-dependency.error.js +2 -3
  17. package/cjs/exception/http-errors/forbidden.error.js +2 -3
  18. package/cjs/exception/http-errors/internal-server.error.js +2 -3
  19. package/cjs/exception/http-errors/method-not-allowed.error.js +2 -3
  20. package/cjs/exception/http-errors/not-acceptable.error.js +2 -3
  21. package/cjs/exception/http-errors/not-found.error.js +2 -3
  22. package/cjs/exception/http-errors/unauthorized.error.js +2 -3
  23. package/cjs/exception/http-errors/unprocessable-entity.error.js +2 -2
  24. package/cjs/exception/opra-exception.js +48 -33
  25. package/cjs/exception/resource-errors/resource-not-found.error.js +2 -3
  26. package/cjs/exception/wrap-exception.js +16 -16
  27. package/cjs/helpers/index.js +1 -1
  28. package/cjs/helpers/is-url-string.js +14 -0
  29. package/cjs/helpers/object-utils.js +2 -2
  30. package/cjs/helpers/responsive-map.js +4 -4
  31. package/cjs/helpers/type-guards.js +15 -3
  32. package/cjs/http/index.js +2 -9
  33. package/cjs/http/opra-url-path.js +251 -0
  34. package/cjs/{url → http}/opra-url.js +53 -109
  35. package/cjs/i18n/i18n.js +1 -1
  36. package/cjs/index.js +0 -2
  37. package/cjs/schema/opra-schema.ns.js +1 -1
  38. package/cjs/schema/resource/operation.interface.js +2 -0
  39. package/esm/document/api-document.js +9 -5
  40. package/esm/document/data-type/api-field.js +2 -2
  41. package/esm/document/data-type/complex-type.js +13 -59
  42. package/esm/document/data-type/data-type.js +0 -9
  43. package/esm/document/data-type/enum-type.js +2 -8
  44. package/esm/document/data-type/mapped-type.js +0 -36
  45. package/esm/document/data-type/simple-type.js +2 -8
  46. package/esm/document/data-type/union-type.js +1 -37
  47. package/esm/document/index.js +2 -0
  48. package/esm/document/resource/collection.js +75 -32
  49. package/esm/document/resource/singleton.js +31 -20
  50. package/esm/document/resource/storage.js +6 -20
  51. package/esm/document/utils/generate-codec.js +33 -0
  52. package/esm/exception/http-errors/bad-request.error.js +2 -3
  53. package/esm/exception/http-errors/failed-dependency.error.js +2 -3
  54. package/esm/exception/http-errors/forbidden.error.js +2 -3
  55. package/esm/exception/http-errors/internal-server.error.js +2 -3
  56. package/esm/exception/http-errors/method-not-allowed.error.js +2 -3
  57. package/esm/exception/http-errors/not-acceptable.error.js +2 -3
  58. package/esm/exception/http-errors/not-found.error.js +2 -3
  59. package/esm/exception/http-errors/unauthorized.error.js +2 -3
  60. package/esm/exception/http-errors/unprocessable-entity.error.js +2 -2
  61. package/esm/exception/opra-exception.js +47 -32
  62. package/esm/exception/resource-errors/resource-not-found.error.js +2 -3
  63. package/esm/exception/wrap-exception.js +16 -16
  64. package/esm/helpers/index.js +1 -1
  65. package/esm/helpers/is-url-string.js +9 -0
  66. package/esm/helpers/object-utils.js +2 -2
  67. package/esm/helpers/responsive-map.js +4 -4
  68. package/esm/helpers/type-guards.js +11 -2
  69. package/esm/http/index.js +2 -9
  70. package/esm/http/opra-url-path.js +246 -0
  71. package/esm/{url → http}/opra-url.js +53 -109
  72. package/esm/i18n/i18n.js +2 -2
  73. package/esm/index.js +0 -2
  74. package/esm/schema/opra-schema.ns.js +1 -1
  75. package/esm/schema/resource/operation.interface.js +1 -0
  76. package/package.json +5 -5
  77. package/types/document/api-document.d.ts +4 -1
  78. package/types/document/data-type/complex-type.d.ts +7 -12
  79. package/types/document/data-type/data-type.d.ts +0 -6
  80. package/types/document/data-type/enum-type.d.ts +2 -4
  81. package/types/document/data-type/mapped-type.d.ts +1 -5
  82. package/types/document/data-type/simple-type.d.ts +2 -4
  83. package/types/document/data-type/union-type.d.ts +1 -5
  84. package/types/document/index.d.ts +2 -0
  85. package/types/document/interfaces/collection-resource.interface.d.ts +10 -0
  86. package/types/document/interfaces/singleton-resource.interface.d.ts +7 -0
  87. package/types/document/interfaces/storage-resource.interface.d.ts +8 -0
  88. package/types/document/resource/collection.d.ts +41 -35
  89. package/types/document/resource/resource.d.ts +1 -0
  90. package/types/document/resource/singleton.d.ts +24 -21
  91. package/types/document/resource/storage.d.ts +14 -17
  92. package/types/document/utils/generate-codec.d.ts +10 -0
  93. package/types/exception/error-issue.d.ts +2 -1
  94. package/types/exception/http-errors/bad-request.error.d.ts +2 -1
  95. package/types/exception/http-errors/failed-dependency.error.d.ts +2 -1
  96. package/types/exception/http-errors/forbidden.error.d.ts +2 -1
  97. package/types/exception/http-errors/internal-server.error.d.ts +2 -1
  98. package/types/exception/http-errors/method-not-allowed.error.d.ts +2 -1
  99. package/types/exception/http-errors/not-acceptable.error.d.ts +2 -1
  100. package/types/exception/http-errors/not-found.error.d.ts +2 -1
  101. package/types/exception/http-errors/unauthorized.error.d.ts +2 -1
  102. package/types/exception/http-errors/unprocessable-entity.error.d.ts +2 -1
  103. package/types/exception/opra-exception.d.ts +13 -8
  104. package/types/exception/wrap-exception.d.ts +1 -1
  105. package/types/helpers/index.d.ts +1 -1
  106. package/types/helpers/is-url-string.d.ts +2 -0
  107. package/types/helpers/object-utils.d.ts +1 -1
  108. package/types/helpers/type-guards.d.ts +3 -0
  109. package/types/http/index.d.ts +2 -9
  110. package/types/http/opra-url-path.d.ts +54 -0
  111. package/types/{url → http}/opra-url.d.ts +9 -13
  112. package/types/index.d.ts +0 -2
  113. package/types/schema/data-type/complex-type.interface.d.ts +1 -1
  114. package/types/schema/opra-schema.ns.d.ts +1 -1
  115. package/types/schema/resource/collection.interface.d.ts +29 -8
  116. package/types/schema/resource/operation.interface.d.ts +3 -0
  117. package/types/schema/resource/singleton.interface.d.ts +5 -5
  118. package/types/schema/resource/storage.interface.d.ts +42 -5
  119. package/cjs/helpers/is-url.js +0 -8
  120. package/cjs/http/codecs/boolean-codec.js +0 -24
  121. package/cjs/http/codecs/date-codec.js +0 -41
  122. package/cjs/http/codecs/filter-codec.js +0 -17
  123. package/cjs/http/codecs/integer-codec.js +0 -19
  124. package/cjs/http/codecs/number-codec.js +0 -24
  125. package/cjs/http/codecs/string-codec.js +0 -23
  126. package/cjs/http/http-params.js +0 -353
  127. package/cjs/http/multipart/batch-multipart.js +0 -170
  128. package/cjs/http/multipart/http-request-content.js +0 -17
  129. package/cjs/http/multipart/http-response-content.js +0 -14
  130. package/cjs/http/multipart/index.js +0 -2
  131. package/cjs/url/index.js +0 -8
  132. package/cjs/url/opra-url-path-component.js +0 -30
  133. package/cjs/url/opra-url-path.js +0 -155
  134. package/cjs/url/utils/decode-path-component.js +0 -41
  135. package/cjs/url/utils/encode-path-component.js +0 -27
  136. package/cjs/utils/path-utils.js +0 -24
  137. package/esm/helpers/is-url.js +0 -4
  138. package/esm/http/codecs/boolean-codec.js +0 -20
  139. package/esm/http/codecs/date-codec.js +0 -37
  140. package/esm/http/codecs/filter-codec.js +0 -13
  141. package/esm/http/codecs/integer-codec.js +0 -15
  142. package/esm/http/codecs/number-codec.js +0 -20
  143. package/esm/http/codecs/string-codec.js +0 -19
  144. package/esm/http/http-params.js +0 -348
  145. package/esm/http/multipart/batch-multipart.js +0 -170
  146. package/esm/http/multipart/http-request-content.js +0 -17
  147. package/esm/http/multipart/http-response-content.js +0 -14
  148. package/esm/http/multipart/index.js +0 -2
  149. package/esm/url/index.js +0 -5
  150. package/esm/url/opra-url-path-component.js +0 -26
  151. package/esm/url/opra-url-path.js +0 -151
  152. package/esm/url/utils/decode-path-component.js +0 -37
  153. package/esm/url/utils/encode-path-component.js +0 -22
  154. package/esm/utils/path-utils.js +0 -19
  155. package/types/helpers/is-url.d.ts +0 -1
  156. package/types/http/codecs/boolean-codec.d.ts +0 -5
  157. package/types/http/codecs/date-codec.d.ts +0 -16
  158. package/types/http/codecs/filter-codec.d.ts +0 -6
  159. package/types/http/codecs/integer-codec.d.ts +0 -11
  160. package/types/http/codecs/number-codec.d.ts +0 -14
  161. package/types/http/codecs/string-codec.d.ts +0 -16
  162. package/types/http/http-params.d.ts +0 -114
  163. package/types/http/interfaces/client-http-headers.interface.d.ts +0 -65
  164. package/types/http/interfaces/server-http-headers.interface.d.ts +0 -1
  165. package/types/http/multipart/batch-multipart.d.ts +0 -0
  166. package/types/http/multipart/http-request-content.d.ts +0 -0
  167. package/types/http/multipart/http-response-content.d.ts +0 -0
  168. package/types/http/multipart/index.d.ts +0 -0
  169. package/types/schema/resource/endpoint.interface.d.ts +0 -29
  170. package/types/url/index.d.ts +0 -5
  171. package/types/url/opra-url-path-component.d.ts +0 -15
  172. package/types/url/opra-url-path.d.ts +0 -36
  173. package/types/url/utils/decode-path-component.d.ts +0 -5
  174. package/types/url/utils/encode-path-component.d.ts +0 -1
  175. package/types/utils/path-utils.d.ts +0 -2
  176. /package/cjs/{http/interfaces/client-http-headers.interface.js → document/interfaces/collection-resource.interface.js} +0 -0
  177. /package/cjs/{http/interfaces/server-http-headers.interface.js → document/interfaces/singleton-resource.interface.js} +0 -0
  178. /package/cjs/{schema/resource/endpoint.interface.js → document/interfaces/storage-resource.interface.js} +0 -0
  179. /package/esm/{http/interfaces/client-http-headers.interface.js → document/interfaces/collection-resource.interface.js} +0 -0
  180. /package/esm/{http/interfaces/server-http-headers.interface.js → document/interfaces/singleton-resource.interface.js} +0 -0
  181. /package/esm/{schema/resource/endpoint.interface.js → document/interfaces/storage-resource.interface.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  import 'reflect-metadata';
2
2
  import omit from 'lodash.omit';
3
3
  import merge from 'putil-merge';
4
- import * as vg from 'valgen';
5
4
  import { omitUndefined, ResponsiveMap } from '../../helpers/index.js';
5
+ import { translate } from '../../i18n/index.js';
6
6
  import { OpraSchema } from '../../schema/index.js';
7
7
  import { METADATA_KEY, TYPENAME_PATTERN } from '../constants.js';
8
8
  import { ApiField } from './api-field.js';
@@ -15,7 +15,7 @@ class ComplexTypeClass extends DataType {
15
15
  super(document, init);
16
16
  this.kind = OpraSchema.ComplexType.Kind;
17
17
  const own = this.own = {};
18
- own.ctor = init?.ctor;
18
+ own.ctor = init?.ctor || init?.base?.ctor;
19
19
  own.abstract = init?.abstract;
20
20
  own.additionalFields = init?.additionalFields;
21
21
  own.fields = new ResponsiveMap();
@@ -28,8 +28,6 @@ class ComplexTypeClass extends DataType {
28
28
  if (this.base) {
29
29
  if (this.additionalFields == null)
30
30
  this.additionalFields = this.base.additionalFields;
31
- if (own.ctor == null && this.base instanceof ComplexType)
32
- this.ctor = this.base.ctor;
33
31
  if (this.base.fields)
34
32
  for (const [k, el] of this.base.fields.entries()) {
35
33
  const newEl = new ApiField(this, el);
@@ -65,7 +63,7 @@ class ComplexTypeClass extends DataType {
65
63
  else
66
64
  field = this.fields.get(nameOrPath);
67
65
  if (!field)
68
- throw new Error(`Unknown field "${nameOrPath}"`);
66
+ throw new Error(translate('error:UNKNOWN_FIELD', { field: nameOrPath }));
69
67
  return field;
70
68
  }
71
69
  iteratePath(path, silent) {
@@ -99,7 +97,7 @@ class ComplexTypeClass extends DataType {
99
97
  if (dataType && !dataType.additionalFields) {
100
98
  if (silent)
101
99
  return { done: true, value: [] };
102
- throw new Error(`Unknown or Invalid field (${curPath})`);
100
+ throw new Error(translate('error:UNKNOWN_FIELD', { field: curPath }));
103
101
  }
104
102
  }
105
103
  }
@@ -111,13 +109,15 @@ class ComplexTypeClass extends DataType {
111
109
  };
112
110
  }
113
111
  normalizeFieldPath(fieldPaths) {
114
- if (Array.isArray(fieldPaths))
115
- return fieldPaths.map((s) => this.normalizeFieldPath(s));
116
- let curPath = '';
117
- for (const [, , p] of this.iteratePath(fieldPaths)) {
118
- curPath = p;
119
- }
120
- return curPath;
112
+ const array = (Array.isArray(fieldPaths) ? fieldPaths : [fieldPaths])
113
+ .map(s => {
114
+ let curPath = '';
115
+ for (const [, , p] of this.iteratePath(s)) {
116
+ curPath = p;
117
+ }
118
+ return curPath;
119
+ }).flat();
120
+ return array.length ? array : undefined;
121
121
  }
122
122
  exportSchema() {
123
123
  const out = super.exportSchema();
@@ -147,52 +147,6 @@ class ComplexTypeClass extends DataType {
147
147
  }
148
148
  return false;
149
149
  }
150
- decode(v) {
151
- return this._getDecoder()(v, { coerce: true });
152
- }
153
- encode(v) {
154
- return this._getEncoder()(v, { coerce: true });
155
- }
156
- validate(v) {
157
- return this._getEncoder()(v);
158
- }
159
- _getDecoder() {
160
- if (this._decoder)
161
- return this._decoder;
162
- const schema = {};
163
- for (const f of this.fields.values()) {
164
- let t = f.type._getDecoder();
165
- if (f.isArray)
166
- t = vg.isArray(t);
167
- schema[f.name] = f.required ? vg.required(t) : vg.optional(t);
168
- }
169
- this._decoder = vg.isObject(schema, {
170
- ctor: this.ctor,
171
- additionalFields: this.additionalFields ?? 'ignore',
172
- name: this.name,
173
- caseInSensitive: true,
174
- });
175
- return this._decoder;
176
- }
177
- _getEncoder() {
178
- if (this._encoder)
179
- return this._encoder;
180
- const schema = {};
181
- for (const f of this.fields.values()) {
182
- let t = f.type._getEncoder();
183
- if (f.isArray)
184
- t = vg.isArray(t);
185
- schema[f.name] = t;
186
- }
187
- this._encoder = vg.isObject(schema, {
188
- ctor: this.ctor,
189
- additionalFields: this.additionalFields,
190
- name: this.name,
191
- caseInSensitive: true,
192
- detectCircular: true
193
- });
194
- return this._encoder;
195
- }
196
150
  }
197
151
  /**
198
152
  * @class ComplexType
@@ -8,15 +8,6 @@ export class DataType {
8
8
  this.description = init?.description;
9
9
  this.isAnonymous = !this.name;
10
10
  }
11
- decode(v) {
12
- return this._getDecoder()(v, { coerce: true });
13
- }
14
- encode(v) {
15
- return this._getEncoder()(v, { coerce: true });
16
- }
17
- validate(v) {
18
- return this._getEncoder()(v);
19
- }
20
11
  exportSchema() {
21
12
  return omitUndefined({
22
13
  kind: this.kind,
@@ -15,6 +15,8 @@ class EnumTypeClass extends DataType {
15
15
  this.ownMeanings = init.meanings || {};
16
16
  this.values = { ...this.base?.values, ...this.ownValues };
17
17
  this.meanings = { ...this.base?.meanings, ...this.ownMeanings };
18
+ this.decode = vg.isEnum(Object.values(this.values));
19
+ this.encode = vg.isEnum(Object.values(this.values));
18
20
  }
19
21
  exportSchema() {
20
22
  const out = DataType.prototype.exportSchema.call(this);
@@ -26,14 +28,6 @@ class EnumTypeClass extends DataType {
26
28
  }));
27
29
  return out;
28
30
  }
29
- _getDecoder() {
30
- if (!this._decoder)
31
- this._decoder = vg.isEnum(Object.values(this.values), { enumName: this.name });
32
- return this._decoder;
33
- }
34
- _getEncoder() {
35
- return this._getDecoder();
36
- }
37
31
  }
38
32
  /**
39
33
  * @class EnumType
@@ -1,6 +1,5 @@
1
1
  import 'reflect-metadata';
2
2
  import merge from 'putil-merge';
3
- import * as vg from 'valgen';
4
3
  import { inheritPropertyInitializers, mergePrototype, omitUndefined, ResponsiveMap } from '../../helpers/index.js';
5
4
  import { OpraSchema } from '../../schema/index.js';
6
5
  import { METADATA_KEY } from '../constants.js';
@@ -33,41 +32,6 @@ class MappedTypeClass extends DataType {
33
32
  }));
34
33
  return out;
35
34
  }
36
- _getDecoder() {
37
- if (this._decoder)
38
- return this._decoder;
39
- const schema = {};
40
- for (const f of this.fields.values()) {
41
- let t = f.type.getDecoder();
42
- if (f.isArray)
43
- t = vg.isArray(t);
44
- schema[f.name] = t;
45
- }
46
- this._decoder = vg.isObject(schema, {
47
- additionalFields: this.additionalFields,
48
- name: this.name,
49
- caseInSensitive: true
50
- });
51
- return this._decoder;
52
- }
53
- _getEncoder() {
54
- if (this._encoder)
55
- return this._encoder;
56
- const schema = {};
57
- for (const f of this.fields.values()) {
58
- let t = f.type.getEncoder();
59
- if (f.isArray)
60
- t = vg.isArray(t);
61
- schema[f.name] = t;
62
- }
63
- this._encoder = vg.isObject(schema, {
64
- additionalFields: this.additionalFields,
65
- name: this.name,
66
- caseInSensitive: true,
67
- detectCircular: true
68
- });
69
- return this._encoder;
70
- }
71
35
  }
72
36
  /**
73
37
  * @class MappedType
@@ -13,14 +13,8 @@ class SimpleTypeClass extends DataType {
13
13
  super(document, init);
14
14
  this.kind = OpraSchema.SimpleType.Kind;
15
15
  this.base = init.base;
16
- this._decoder = init.decoder || init.base?._decoder || vg.isAny();
17
- this._encoder = init.encoder || init.base?._encoder || vg.isAny();
18
- }
19
- _getDecoder() {
20
- return this._decoder;
21
- }
22
- _getEncoder() {
23
- return this._encoder;
16
+ this.decode = init.decoder || init.base?.decode || vg.isAny();
17
+ this.encode = init.encoder || init.base?.encode || vg.isAny();
24
18
  }
25
19
  exportSchema() {
26
20
  // noinspection UnnecessaryLocalVariableJS
@@ -1,6 +1,5 @@
1
1
  import 'reflect-metadata';
2
2
  import merge from 'putil-merge';
3
- import * as vg from 'valgen';
4
3
  import { inheritPropertyInitializers, mergePrototype, omitUndefined, ResponsiveMap } from '../../helpers/index.js';
5
4
  import { OpraSchema } from '../../schema/index.js';
6
5
  import { METADATA_KEY } from '../constants.js';
@@ -20,7 +19,7 @@ class UnionTypeClass extends DataType {
20
19
  `${OpraSchema.UnionType.Kind} of ${OpraSchema.MappedType.Kind} types.`);
21
20
  own.types.push(base);
22
21
  if (base.additionalFields)
23
- this.additionalFields = true;
22
+ this.additionalFields = base.additionalFields;
24
23
  this.fields.setAll(base.fields);
25
24
  }
26
25
  this.types = [...own.types];
@@ -32,41 +31,6 @@ class UnionTypeClass extends DataType {
32
31
  }));
33
32
  return out;
34
33
  }
35
- _getDecoder() {
36
- if (this._decoder)
37
- return this._decoder;
38
- const schema = {};
39
- for (const f of this.fields.values()) {
40
- let t = f.type.getDecoder();
41
- if (f.isArray)
42
- t = vg.isArray(t);
43
- schema[f.name] = t;
44
- }
45
- this._decoder = vg.isObject(schema, {
46
- additionalFields: this.additionalFields,
47
- name: this.name,
48
- caseInSensitive: true
49
- });
50
- return this._decoder;
51
- }
52
- _getEncoder() {
53
- if (this._encoder)
54
- return this._encoder;
55
- const schema = {};
56
- for (const f of this.fields.values()) {
57
- let t = f.type.getEncoder();
58
- if (f.isArray)
59
- t = vg.isArray(t);
60
- schema[f.name] = t;
61
- }
62
- this._encoder = vg.isObject(schema, {
63
- additionalFields: this.additionalFields,
64
- name: this.name,
65
- caseInSensitive: true,
66
- detectCircular: true
67
- });
68
- return this._encoder;
69
- }
70
34
  }
71
35
  /**
72
36
  * @class UnionType
@@ -13,3 +13,5 @@ export * from './resource/resource.js';
13
13
  export * from './resource/collection.js';
14
14
  export * from './resource/singleton.js';
15
15
  export * from './resource/storage.js';
16
+ export * from './interfaces/collection-resource.interface.js';
17
+ export * from './interfaces/singleton-resource.interface.js';
@@ -1,5 +1,6 @@
1
1
  import omit from 'lodash.omit';
2
2
  import merge from 'putil-merge';
3
+ import * as vg from 'valgen';
3
4
  import { BadRequestError } from '../../exception/index.js';
4
5
  import { OpraFilter } from '../../filter/index.js';
5
6
  import { omitUndefined } from '../../helpers/index.js';
@@ -7,15 +8,18 @@ import { translate } from '../../i18n/index.js';
7
8
  import { OpraSchema } from '../../schema/index.js';
8
9
  import { METADATA_KEY } from '../constants.js';
9
10
  import { SimpleType } from '../data-type/simple-type.js';
11
+ import { generateCodec } from '../utils/generate-codec.js';
10
12
  import { Resource } from './resource.js';
11
- const NESTJS_INJECTABLE_WATERMARK = '__injectable__';
13
+ const NESTJS_INJECTABLE_WATERMARK = '__injectable__'; // todo, put this in nextjs package wia augmentation
12
14
  const NAME_PATTERN = /^(.*)(Resource|Collection)$/;
13
15
  class CollectionClass extends Resource {
14
16
  constructor(document, init) {
15
17
  super(document, init);
18
+ this._decoders = {};
19
+ this._encoders = {};
16
20
  this.kind = OpraSchema.Collection.Kind;
17
21
  this.controller = init.controller;
18
- const operations = this.operations = init.operations || {};
22
+ this.operations = { ...init.operations };
19
23
  const dataType = this.type = init.type;
20
24
  // Validate key fields
21
25
  this.primaryKey = init.primaryKey
@@ -28,19 +32,6 @@ class CollectionClass extends Resource {
28
32
  if (!(field?.type instanceof SimpleType))
29
33
  throw new TypeError(`Only Simple type allowed for primary keys but "${f}" is a ${field.type.kind}`);
30
34
  });
31
- if (this.controller) {
32
- const instance = typeof this.controller == 'function'
33
- ? new this.controller()
34
- : this.controller;
35
- for (const operation of Object.values(operations)) {
36
- if (!operation.handler && operation.handlerName) {
37
- const fn = instance[operation.handlerName];
38
- if (!fn)
39
- throw new TypeError(`No such operation handler (${operation.handlerName}) found`);
40
- operation.handler = fn.bind(instance);
41
- }
42
- }
43
- }
44
35
  }
45
36
  exportSchema() {
46
37
  const out = Resource.prototype.exportSchema.call(this);
@@ -66,7 +57,8 @@ class CollectionClass extends Resource {
66
57
  // decode values
67
58
  for (const [k, v] of Object.entries(obj)) {
68
59
  const el = dataType.getField(k);
69
- obj[k] = el.type.decode(v);
60
+ if (el.type instanceof SimpleType)
61
+ obj[k] = el.type.decode(v);
70
62
  if (obj[k] == null)
71
63
  throw new TypeError(`You must provide value of primary field(s) (${k})`);
72
64
  }
@@ -76,7 +68,9 @@ class CollectionClass extends Resource {
76
68
  if (typeof value === 'object')
77
69
  value = value[primaryKey];
78
70
  const el = dataType.getField(primaryKey);
79
- const result = el.type.decode(value);
71
+ let result;
72
+ if (el.type instanceof SimpleType)
73
+ result = el.type.decode(value);
80
74
  if (result == null)
81
75
  throw new TypeError(`You must provide value of primary field(s) (${primaryKey})`);
82
76
  return result;
@@ -87,12 +81,14 @@ class CollectionClass extends Resource {
87
81
  }
88
82
  normalizeSortFields(fields) {
89
83
  const normalized = this.type.normalizeFieldPath(fields);
90
- const findManyEndpoint = this.operations.findMany;
91
- const sortFields = findManyEndpoint && findManyEndpoint.sortFields;
84
+ if (!normalized)
85
+ return;
86
+ const findManyOp = this.operations.findMany;
87
+ const sortFields = findManyOp && findManyOp.sortFields;
92
88
  (Array.isArray(normalized) ? normalized : [normalized]).forEach(field => {
93
89
  if (!sortFields?.find(x => x === field))
94
90
  throw new BadRequestError({
95
- message: translate('error:UNACCEPTED_SORT_FIELD', { field }, `Field '${field}' is not available for sort operation`),
91
+ message: translate('error:UNACCEPTED_SORT_FIELD', { field }),
96
92
  });
97
93
  });
98
94
  return normalized;
@@ -105,27 +101,75 @@ class CollectionClass extends Resource {
105
101
  this.normalizeFilter(ast.left);
106
102
  if (!(ast.left instanceof OpraFilter.QualifiedIdentifier && ast.left.field))
107
103
  throw new TypeError(`Invalid filter query. Left side should be a data field.`);
104
+ // Check if filtering accepted for given field
105
+ const findManyOp = this.operations.findMany;
106
+ const fieldLower = ast.left.value.toLowerCase();
107
+ const filterDef = (findManyOp && findManyOp.filters || [])
108
+ .find(f => f.field.toLowerCase() === fieldLower);
109
+ if (!filterDef) {
110
+ throw new BadRequestError({
111
+ message: translate('error:UNACCEPTED_FILTER_FIELD', { field: ast.left.value }),
112
+ });
113
+ }
114
+ // Check if filtering operation accepted for given field
115
+ if (!filterDef.operators?.includes(ast.op))
116
+ throw new BadRequestError({
117
+ message: translate('error:UNACCEPTED_FILTER_OPERATION', { field: ast.left.value }),
118
+ });
108
119
  this.normalizeFilter(ast.right);
120
+ return ast;
109
121
  }
110
- else if (ast instanceof OpraFilter.LogicalExpression) {
122
+ if (ast instanceof OpraFilter.LogicalExpression) {
111
123
  ast.items.forEach(item => this.normalizeFilter(item));
124
+ return ast;
112
125
  }
113
- else if (ast instanceof OpraFilter.ArithmeticExpression) {
126
+ if (ast instanceof OpraFilter.ArithmeticExpression) {
114
127
  ast.items.forEach(item => this.normalizeFilter(item.expression));
128
+ return ast;
115
129
  }
116
- else if (ast instanceof OpraFilter.ArrayExpression) {
130
+ if (ast instanceof OpraFilter.ArrayExpression) {
117
131
  ast.items.forEach(item => this.normalizeFilter(item));
132
+ return ast;
118
133
  }
119
- else if (ast instanceof OpraFilter.ParenthesizedExpression) {
134
+ if (ast instanceof OpraFilter.ParenthesizedExpression) {
120
135
  this.normalizeFilter(ast.expression);
136
+ return ast;
121
137
  }
122
- else if (ast instanceof OpraFilter.QualifiedIdentifier) {
123
- ast.field = this.type.findField(ast.value);
138
+ if (ast instanceof OpraFilter.QualifiedIdentifier) {
139
+ const normalizedFieldPath = this.type.normalizeFieldPath(ast.value)?.join('.');
140
+ ast.field = this.type.getField(normalizedFieldPath);
124
141
  ast.dataType = ast.field?.type || this.document.getDataType('any');
125
- ast.value = this.type.normalizeFieldPath(ast.value);
142
+ ast.value = normalizedFieldPath;
143
+ return ast;
126
144
  }
127
145
  return ast;
128
146
  }
147
+ getDecoder(operation) {
148
+ let decoder = this._decoders[operation];
149
+ if (decoder)
150
+ return decoder;
151
+ const options = {
152
+ partial: operation !== 'create'
153
+ };
154
+ if (operation !== 'create')
155
+ options.omit = [...this.primaryKey];
156
+ decoder = generateCodec(this.type, 'decode', options);
157
+ this._decoders[operation] = decoder;
158
+ return decoder;
159
+ }
160
+ getEncoder(operation) {
161
+ let encoder = this._encoders[operation];
162
+ if (encoder)
163
+ return encoder;
164
+ const options = {
165
+ partial: true
166
+ };
167
+ encoder = generateCodec(this.type, 'encode', options);
168
+ if (operation === 'findMany')
169
+ return vg.isArray(encoder);
170
+ this._encoders[operation] = encoder;
171
+ return encoder;
172
+ }
129
173
  }
130
174
  /**
131
175
  *
@@ -163,13 +207,12 @@ export const Collection = function (...args) {
163
207
  Collection.prototype = CollectionClass.prototype;
164
208
  function createOperationDecorator(operation) {
165
209
  return (options) => ((target, propertyKey) => {
166
- const metadata = {
167
- ...options,
168
- handlerName: propertyKey
169
- };
210
+ if (propertyKey !== operation)
211
+ throw new TypeError(`Name of the handler name should be '${operation}'`);
212
+ const operationMeta = { ...options };
170
213
  const resourceMetadata = (Reflect.getOwnMetadata(METADATA_KEY, target.constructor) || {});
171
214
  resourceMetadata.operations = resourceMetadata.operations || {};
172
- resourceMetadata.operations[operation] = metadata;
215
+ resourceMetadata.operations[operation] = operationMeta;
173
216
  Reflect.defineMetadata(METADATA_KEY, resourceMetadata, target.constructor);
174
217
  });
175
218
  }
@@ -3,29 +3,19 @@ import merge from 'putil-merge';
3
3
  import { omitUndefined } from '../../helpers/index.js';
4
4
  import { OpraSchema } from '../../schema/index.js';
5
5
  import { METADATA_KEY } from '../constants.js';
6
+ import { generateCodec } from '../utils/generate-codec.js';
6
7
  import { Resource } from './resource.js';
7
- const NESTJS_INJECTABLE_WATERMARK = '__injectable__';
8
+ const NESTJS_INJECTABLE_WATERMARK = '__injectable__'; // todo, put this in nextjs package wia augmentation
8
9
  const NAME_PATTERN = /^(.*)(Resource|Singleton)$/;
9
10
  class SingletonClass extends Resource {
10
11
  constructor(document, init) {
11
12
  super(document, init);
13
+ this._decoders = {};
14
+ this._encoders = {};
12
15
  this.kind = OpraSchema.Singleton.Kind;
13
16
  this.controller = init.controller;
14
- const operations = this.operations = init.operations || {};
17
+ this.operations = { ...init.operations };
15
18
  this.type = init.type;
16
- if (this.controller) {
17
- const instance = typeof this.controller == 'function'
18
- ? new this.controller()
19
- : this.controller;
20
- for (const operation of Object.values(operations)) {
21
- if (!operation.handler && operation.handlerName) {
22
- const fn = instance[operation.handlerName];
23
- if (!fn)
24
- throw new TypeError(`No such operation handler (${operation.handlerName}) found`);
25
- operation.handler = fn.bind(instance);
26
- }
27
- }
28
- }
29
19
  }
30
20
  exportSchema() {
31
21
  const out = Resource.prototype.exportSchema.call(this);
@@ -38,6 +28,28 @@ class SingletonClass extends Resource {
38
28
  normalizeFieldPath(path) {
39
29
  return this.type.normalizeFieldPath(path);
40
30
  }
31
+ getDecoder(operation) {
32
+ let decoder = this._decoders[operation];
33
+ if (decoder)
34
+ return decoder;
35
+ const options = {
36
+ partial: operation !== 'create'
37
+ };
38
+ decoder = generateCodec(this.type, 'decode', options);
39
+ this._decoders[operation] = decoder;
40
+ return decoder;
41
+ }
42
+ getEncoder(operation) {
43
+ let encoder = this._encoders[operation];
44
+ if (encoder)
45
+ return encoder;
46
+ const options = {
47
+ partial: true
48
+ };
49
+ encoder = generateCodec(this.type, 'encode', options);
50
+ this._encoders[operation] = encoder;
51
+ return encoder;
52
+ }
41
53
  }
42
54
  export const Singleton = function (...args) {
43
55
  // ClassDecorator
@@ -68,13 +80,12 @@ export const Singleton = function (...args) {
68
80
  Singleton.prototype = SingletonClass.prototype;
69
81
  function createOperationDecorator(operation) {
70
82
  return (options) => ((target, propertyKey) => {
71
- const metadata = {
72
- ...options,
73
- handlerName: propertyKey
74
- };
83
+ if (propertyKey !== operation)
84
+ throw new TypeError(`Name of the handler name should be '${operation}'`);
85
+ const operationMeta = { ...options };
75
86
  const resourceMetadata = (Reflect.getOwnMetadata(METADATA_KEY, target.constructor) || {});
76
87
  resourceMetadata.operations = resourceMetadata.operations || {};
77
- resourceMetadata.operations[operation] = metadata;
88
+ resourceMetadata.operations[operation] = operationMeta;
78
89
  Reflect.defineMetadata(METADATA_KEY, resourceMetadata, target.constructor);
79
90
  });
80
91
  }
@@ -11,20 +11,7 @@ class StorageClass extends Resource {
11
11
  super(document, init);
12
12
  this.kind = OpraSchema.Storage.Kind;
13
13
  this.controller = init.controller;
14
- const operations = this.operations = init.operations || {};
15
- if (this.controller) {
16
- const instance = typeof this.controller == 'function'
17
- ? new this.controller()
18
- : this.controller;
19
- for (const operation of Object.values(operations)) {
20
- if (!operation.handler && operation.handlerName) {
21
- const fn = instance[operation.handlerName];
22
- if (!fn)
23
- throw new TypeError(`No such operation handler (${operation.handlerName}) found`);
24
- operation.handler = fn.bind(instance);
25
- }
26
- }
27
- }
14
+ this.operations = { ...init.operations };
28
15
  }
29
16
  exportSchema() {
30
17
  const out = Resource.prototype.exportSchema.call(this);
@@ -62,16 +49,15 @@ export const Storage = function (...args) {
62
49
  Storage.prototype = StorageClass.prototype;
63
50
  function createOperationDecorator(operation) {
64
51
  return (options) => ((target, propertyKey) => {
65
- const metadata = {
66
- ...options,
67
- handlerName: propertyKey
68
- };
52
+ if (propertyKey !== operation)
53
+ throw new TypeError(`Name of the handler name should be '${operation}'`);
54
+ const operationMeta = { ...options };
69
55
  const resourceMetadata = (Reflect.getOwnMetadata(METADATA_KEY, target.constructor) || {});
70
56
  resourceMetadata.operations = resourceMetadata.operations || {};
71
- resourceMetadata.operations[operation] = metadata;
57
+ resourceMetadata.operations[operation] = operationMeta;
72
58
  Reflect.defineMetadata(METADATA_KEY, resourceMetadata, target.constructor);
73
59
  });
74
60
  }
75
61
  Storage.Delete = createOperationDecorator('delete');
76
62
  Storage.Get = createOperationDecorator('get');
77
- Storage.Put = createOperationDecorator('put');
63
+ Storage.Post = createOperationDecorator('post');
@@ -0,0 +1,33 @@
1
+ import * as vg from 'valgen';
2
+ import { ComplexType } from '../data-type/complex-type.js';
3
+ import { EnumType } from '../data-type/enum-type.js';
4
+ import { MappedType } from '../data-type/mapped-type.js';
5
+ import { SimpleType } from '../data-type/simple-type.js';
6
+ import { UnionType } from '../data-type/union-type.js';
7
+ export function generateCodec(type, codec, options) {
8
+ return _generateDecoder(type, codec, options);
9
+ }
10
+ export function _generateDecoder(type, codec, options) {
11
+ const schema = {};
12
+ for (const f of type.fields.values()) {
13
+ let fn;
14
+ if (f.type instanceof SimpleType || f.type instanceof EnumType) {
15
+ fn = f.type[codec];
16
+ }
17
+ else if (f.type instanceof ComplexType || f.type instanceof MappedType || f.type instanceof UnionType) {
18
+ fn = _generateDecoder(f.type, codec, options);
19
+ }
20
+ /* istanbul ignore next */
21
+ if (!fn)
22
+ throw new TypeError(`Can't generate codec for (${f.type})`);
23
+ if (f.isArray)
24
+ fn = vg.isArray(fn);
25
+ schema[f.name] = !options.partial && f.required ? vg.required(fn) : vg.optional(fn);
26
+ }
27
+ return vg.isObject(schema, {
28
+ ctor: type.ctor,
29
+ additionalFields: type.additionalFields ?? false,
30
+ name: type.name,
31
+ caseInSensitive: true,
32
+ });
33
+ }
@@ -10,10 +10,9 @@ export class BadRequestError extends OpraException {
10
10
  super(...arguments);
11
11
  this.status = 400;
12
12
  }
13
- setIssue(issue) {
14
- super.setIssue({
13
+ init(issue) {
14
+ super.init({
15
15
  message: translate('error:BAD_REQUEST', 'Bad request'),
16
- severity: 'error',
17
16
  code: 'BAD_REQUEST',
18
17
  ...issue
19
18
  });
@@ -9,10 +9,9 @@ export class FailedDependencyError extends OpraException {
9
9
  super(...arguments);
10
10
  this.status = 424;
11
11
  }
12
- setIssue(issue) {
13
- super.setIssue({
12
+ init(issue) {
13
+ super.init({
14
14
  message: translate('error:FAILED_DEPENDENCY', 'The request failed due to failure of a previous request'),
15
- severity: 'error',
16
15
  code: 'FAILED_DEPENDENCY',
17
16
  ...issue
18
17
  });