@opra/common 0.25.5 → 0.26.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 (197) hide show
  1. package/browser.js +1826 -1338
  2. package/cjs/document/api-document.js +16 -220
  3. package/cjs/document/data-type/complex-type-class.js +41 -19
  4. package/cjs/document/data-type/complex-type.js +1 -1
  5. package/cjs/document/data-type/data-type.js +2 -1
  6. package/cjs/document/data-type/enum-type-class.js +15 -12
  7. package/cjs/document/data-type/enum-type.js +28 -14
  8. package/cjs/document/data-type/field-class.js +21 -3
  9. package/cjs/document/data-type/field-decorator.js +0 -19
  10. package/cjs/document/data-type/mapped-type-class.js +8 -10
  11. package/cjs/document/data-type/mapped-type.js +1 -1
  12. package/cjs/document/data-type/simple-type-class.js +17 -8
  13. package/cjs/document/data-type/simple-type.js +1 -1
  14. package/cjs/document/data-type/union-type-class.js +15 -13
  15. package/cjs/document/document-base.js +24 -0
  16. package/cjs/document/factory/api-document-factory.js +231 -0
  17. package/cjs/document/factory/type-document-factory.js +324 -0
  18. package/cjs/document/index.js +7 -1
  19. package/cjs/document/resource/action-decorator.js +27 -0
  20. package/cjs/document/resource/collection-class.js +95 -44
  21. package/cjs/document/resource/collection-decorator.js +182 -0
  22. package/cjs/document/resource/collection.js +4 -4
  23. package/cjs/document/resource/container-class.js +82 -0
  24. package/cjs/document/resource/container-decorator.js +24 -0
  25. package/cjs/document/resource/container.js +29 -0
  26. package/cjs/document/resource/crud-resource.js +31 -0
  27. package/cjs/document/resource/endpoint.js +24 -22
  28. package/cjs/document/resource/operation-decorator.js +27 -0
  29. package/cjs/document/resource/parameter.js +51 -0
  30. package/cjs/document/{decorators/resource.decorator.js → resource/resource-decorator.js} +9 -14
  31. package/cjs/document/resource/resource.js +20 -5
  32. package/cjs/document/resource/singleton-class.js +60 -40
  33. package/cjs/document/resource/singleton-decorator.js +57 -0
  34. package/cjs/document/resource/singleton.js +4 -4
  35. package/cjs/document/resource/storage-class.js +11 -17
  36. package/cjs/document/resource/storage-decorator.js +66 -0
  37. package/cjs/document/resource/storage.js +3 -3
  38. package/cjs/document/type-document.js +195 -0
  39. package/cjs/exception/http-errors/internal-server.error.js +1 -0
  40. package/cjs/exception/wrap-exception.js +2 -1
  41. package/cjs/helpers/responsive-map.js +3 -3
  42. package/cjs/http/opra-url-path.js +2 -1
  43. package/cjs/schema/document/document-base.interface.js +2 -0
  44. package/cjs/schema/document/type-document.interface.js +2 -0
  45. package/cjs/schema/opra-schema.ns.js +3 -1
  46. package/cjs/schema/type-guards.js +3 -3
  47. package/esm/document/api-document.js +16 -220
  48. package/esm/document/data-type/complex-type-class.js +40 -19
  49. package/esm/document/data-type/complex-type.js +1 -1
  50. package/esm/document/data-type/data-type.js +2 -1
  51. package/esm/document/data-type/enum-type-class.js +15 -12
  52. package/esm/document/data-type/enum-type.js +26 -12
  53. package/esm/document/data-type/field-class.js +20 -3
  54. package/esm/document/data-type/field-decorator.js +0 -19
  55. package/esm/document/data-type/mapped-type-class.js +9 -11
  56. package/esm/document/data-type/mapped-type.js +1 -1
  57. package/esm/document/data-type/simple-type-class.js +15 -6
  58. package/esm/document/data-type/simple-type.js +1 -1
  59. package/esm/document/data-type/union-type-class.js +11 -9
  60. package/esm/document/document-base.js +20 -0
  61. package/esm/document/factory/api-document-factory.js +227 -0
  62. package/esm/document/factory/type-document-factory.js +320 -0
  63. package/esm/document/index.js +7 -1
  64. package/esm/document/resource/action-decorator.js +23 -0
  65. package/esm/document/resource/collection-class.js +95 -44
  66. package/esm/document/resource/collection-decorator.js +178 -0
  67. package/esm/document/resource/collection.js +4 -4
  68. package/esm/document/resource/container-class.js +78 -0
  69. package/esm/document/resource/container-decorator.js +20 -0
  70. package/esm/document/resource/container.js +25 -0
  71. package/esm/document/resource/crud-resource.js +27 -0
  72. package/esm/document/resource/endpoint.js +22 -20
  73. package/esm/document/resource/operation-decorator.js +23 -0
  74. package/esm/document/resource/parameter.js +46 -0
  75. package/esm/document/resource/resource-decorator.js +28 -0
  76. package/esm/document/resource/resource.js +20 -5
  77. package/esm/document/resource/singleton-class.js +58 -38
  78. package/esm/document/resource/singleton-decorator.js +53 -0
  79. package/esm/document/resource/singleton.js +4 -4
  80. package/esm/document/resource/storage-class.js +9 -15
  81. package/esm/document/resource/storage-decorator.js +62 -0
  82. package/esm/document/resource/storage.js +3 -3
  83. package/esm/document/type-document.js +191 -0
  84. package/esm/exception/http-errors/internal-server.error.js +1 -0
  85. package/esm/exception/wrap-exception.js +2 -1
  86. package/esm/helpers/responsive-map.js +3 -3
  87. package/esm/http/opra-url-path.js +1 -1
  88. package/esm/schema/document/document-base.interface.js +1 -0
  89. package/esm/schema/document/type-document.interface.js +1 -0
  90. package/esm/schema/opra-schema.ns.js +3 -1
  91. package/esm/schema/type-guards.js +1 -1
  92. package/package.json +4 -4
  93. package/types/document/api-document.d.ts +32 -73
  94. package/types/document/data-type/complex-type-class.d.ts +5 -5
  95. package/types/document/data-type/complex-type.d.ts +8 -3
  96. package/types/document/{decorators → data-type}/complex-type.decorator.d.ts +1 -1
  97. package/types/document/data-type/data-type.d.ts +11 -1
  98. package/types/document/data-type/enum-type-class.d.ts +6 -6
  99. package/types/document/data-type/enum-type.d.ts +12 -6
  100. package/types/document/data-type/field-class.d.ts +9 -1
  101. package/types/document/data-type/field-decorator.d.ts +1 -1
  102. package/types/document/data-type/field.d.ts +4 -3
  103. package/types/document/data-type/mapped-type-class.d.ts +5 -9
  104. package/types/document/data-type/mapped-type.d.ts +6 -7
  105. package/types/document/data-type/simple-type-class.d.ts +6 -3
  106. package/types/document/{decorators → data-type}/simple-type.decorator.d.ts +1 -1
  107. package/types/document/data-type/union-type-class.d.ts +2 -8
  108. package/types/document/data-type/union-type.d.ts +7 -6
  109. package/types/document/document-base.d.ts +10 -0
  110. package/types/document/factory/api-document-factory.d.ts +50 -0
  111. package/types/document/factory/type-document-factory.d.ts +56 -0
  112. package/types/document/index.d.ts +7 -1
  113. package/types/document/resource/action-decorator.d.ts +6 -0
  114. package/types/document/resource/collection-class.d.ts +17 -12
  115. package/types/document/resource/collection-decorator.d.ts +192 -0
  116. package/types/document/resource/collection.d.ts +11 -19
  117. package/types/document/resource/container-class.d.ts +66 -0
  118. package/types/document/resource/container-decorator.d.ts +37 -0
  119. package/types/document/resource/container.d.ts +25 -0
  120. package/types/document/resource/crud-resource.d.ts +16 -0
  121. package/types/document/resource/endpoint.d.ts +21 -21
  122. package/types/document/resource/operation-decorator.d.ts +6 -0
  123. package/types/document/resource/parameter.d.ts +35 -0
  124. package/types/document/resource/resource-decorator.d.ts +32 -0
  125. package/types/document/resource/resource.d.ts +15 -13
  126. package/types/document/resource/singleton-class.d.ts +15 -14
  127. package/types/document/resource/singleton-decorator.d.ts +125 -0
  128. package/types/document/resource/singleton.d.ts +11 -16
  129. package/types/document/resource/storage-class.d.ts +13 -6
  130. package/types/document/resource/storage-decorator.d.ts +98 -0
  131. package/types/document/resource/storage.d.ts +9 -12
  132. package/types/document/type-document.d.ts +68 -0
  133. package/types/http/opra-url-path.d.ts +1 -1
  134. package/types/schema/data-type/complex-type.interface.d.ts +1 -1
  135. package/types/schema/data-type/data-type.interface.d.ts +3 -3
  136. package/types/schema/data-type/enum-type.interface.d.ts +6 -7
  137. package/types/schema/data-type/mapped-type.interface.d.ts +4 -3
  138. package/types/schema/data-type/simple-type.interface.d.ts +3 -3
  139. package/types/schema/data-type/union-type.interface.d.ts +6 -4
  140. package/types/schema/document/api-document.interface.d.ts +11 -0
  141. package/types/schema/document/document-base.interface.d.ts +24 -0
  142. package/types/schema/document/type-document.interface.d.ts +6 -0
  143. package/types/schema/opra-schema.ns.d.ts +3 -1
  144. package/types/schema/resource/collection.interface.d.ts +31 -31
  145. package/types/schema/resource/container.interface.d.ts +4 -3
  146. package/types/schema/resource/resource.interface.d.ts +1 -1
  147. package/types/schema/resource/singleton.interface.d.ts +15 -10
  148. package/types/schema/resource/storage.interface.d.ts +49 -45
  149. package/types/schema/type-guards.d.ts +3 -2
  150. package/cjs/document/decorators/build-operation-decorator.js +0 -27
  151. package/cjs/document/decorators/collection-decorator.js +0 -30
  152. package/cjs/document/decorators/singleton.decorator.js +0 -25
  153. package/cjs/document/decorators/storage.decorator.js +0 -27
  154. package/cjs/document/factory/add-references.js +0 -20
  155. package/cjs/document/factory/create-document.js +0 -83
  156. package/cjs/document/factory/factory.js +0 -66
  157. package/cjs/document/factory/import-resource-class.js +0 -54
  158. package/cjs/document/factory/import-type-class.js +0 -146
  159. package/cjs/document/factory/index.js +0 -4
  160. package/cjs/document/factory/process-resources.js +0 -70
  161. package/cjs/document/factory/process-types.js +0 -191
  162. package/cjs/document/utils/generate-codec.js +0 -39
  163. package/esm/document/decorators/build-operation-decorator.js +0 -23
  164. package/esm/document/decorators/collection-decorator.js +0 -26
  165. package/esm/document/decorators/resource.decorator.js +0 -33
  166. package/esm/document/decorators/singleton.decorator.js +0 -21
  167. package/esm/document/decorators/storage.decorator.js +0 -23
  168. package/esm/document/factory/add-references.js +0 -16
  169. package/esm/document/factory/create-document.js +0 -77
  170. package/esm/document/factory/factory.js +0 -62
  171. package/esm/document/factory/import-resource-class.js +0 -48
  172. package/esm/document/factory/import-type-class.js +0 -136
  173. package/esm/document/factory/index.js +0 -1
  174. package/esm/document/factory/process-resources.js +0 -63
  175. package/esm/document/factory/process-types.js +0 -185
  176. package/esm/document/utils/generate-codec.js +0 -33
  177. package/types/document/decorators/build-operation-decorator.d.ts +0 -13
  178. package/types/document/decorators/collection-decorator.d.ts +0 -33
  179. package/types/document/decorators/resource.decorator.d.ts +0 -9
  180. package/types/document/decorators/singleton.decorator.d.ts +0 -25
  181. package/types/document/decorators/storage.decorator.d.ts +0 -25
  182. package/types/document/factory/add-references.d.ts +0 -4
  183. package/types/document/factory/create-document.d.ts +0 -12
  184. package/types/document/factory/factory.d.ts +0 -63
  185. package/types/document/factory/import-resource-class.d.ts +0 -10
  186. package/types/document/factory/import-type-class.d.ts +0 -17
  187. package/types/document/factory/index.d.ts +0 -1
  188. package/types/document/factory/process-resources.d.ts +0 -9
  189. package/types/document/factory/process-types.d.ts +0 -6
  190. package/types/document/utils/generate-codec.d.ts +0 -10
  191. package/types/schema/document.interface.d.ts +0 -34
  192. /package/cjs/document/{decorators → data-type}/complex-type.decorator.js +0 -0
  193. /package/cjs/document/{decorators → data-type}/simple-type.decorator.js +0 -0
  194. /package/cjs/schema/{document.interface.js → document/api-document.interface.js} +0 -0
  195. /package/esm/document/{decorators → data-type}/complex-type.decorator.js +0 -0
  196. /package/esm/document/{decorators → data-type}/simple-type.decorator.js +0 -0
  197. /package/esm/schema/{document.interface.js → document/api-document.interface.js} +0 -0
@@ -0,0 +1,320 @@
1
+ import { validator } from 'valgen';
2
+ import { cloneObject, isConstructor, resolveThunk, ResponsiveMap } from '../../helpers/index.js';
3
+ import { OpraSchema } from '../../schema/index.js';
4
+ import { DATATYPE_METADATA } from '../constants.js';
5
+ import { AnyType, Base64Type, BigintType, BooleanType, DateType, IntegerType, NullType, NumberType, ObjectIdType, ObjectType, StringType, TimestampType, TimeType, UuidType } from '../data-type/builtin/index.js';
6
+ import { ComplexType } from '../data-type/complex-type.js';
7
+ import { EnumType } from '../data-type/enum-type.js';
8
+ import { MappedType } from '../data-type/mapped-type.js';
9
+ import { SimpleType } from '../data-type/simple-type.js';
10
+ import { UnionType } from '../data-type/union-type.js';
11
+ import { TypeDocument } from '../type-document.js';
12
+ /**
13
+ * @class TypeDocumentFactory
14
+ */
15
+ export class TypeDocumentFactory {
16
+ constructor() {
17
+ this.typeQueue = new ResponsiveMap();
18
+ this.circularRefs = new Map();
19
+ this.curPath = [];
20
+ this.cache = new Map();
21
+ }
22
+ /**
23
+ * Creates ApiDocument instance from given schema object
24
+ */
25
+ static async createDocument(init) {
26
+ const factory = new TypeDocumentFactory();
27
+ const document = factory.document = new TypeDocument();
28
+ await factory.initDocument(init);
29
+ return document;
30
+ }
31
+ /**
32
+ * Downloads schema from the given URL and creates the document instance * @param url
33
+ */
34
+ static async createDocumentFromUrl(url) {
35
+ const factory = new TypeDocumentFactory();
36
+ const document = factory.document = new TypeDocument();
37
+ await factory.initDocumentFromUrl(url);
38
+ return document;
39
+ }
40
+ async initDocument(init) {
41
+ this.document.url = init.url;
42
+ if (init.info)
43
+ Object.assign(this.document.info, init.info);
44
+ if (!init?.noBuiltinTypes) {
45
+ const builtinDocument = await this.createBuiltinTypeDocument();
46
+ this.document.references.set('Opra', builtinDocument);
47
+ }
48
+ if (init.references)
49
+ await this.addReferences(init.references);
50
+ if (init.types) {
51
+ this.curPath.push('Types->');
52
+ // Add type sources into typeQueue
53
+ if (Array.isArray(init.types)) {
54
+ let i = 0;
55
+ for (const thunk of init.types) {
56
+ const metadata = Reflect.getMetadata(DATATYPE_METADATA, thunk) || thunk[DATATYPE_METADATA];
57
+ if (!(metadata && metadata.name))
58
+ throw new TypeError(`Metadata information not found at types[${i++}] "${String(thunk)}"`);
59
+ this.typeQueue.set(metadata.name, thunk);
60
+ }
61
+ }
62
+ else
63
+ for (const [name, schema] of Object.entries(init.types)) {
64
+ this.typeQueue.set(name, { ...schema, name });
65
+ }
66
+ // Create type instances
67
+ for (const thunk of this.typeQueue.values()) {
68
+ await this.importDataType(thunk);
69
+ }
70
+ this.document.types.sort();
71
+ this.curPath.pop();
72
+ }
73
+ this.document.invalidate();
74
+ return this.document;
75
+ }
76
+ async initDocumentFromUrl(url) {
77
+ const resp = await fetch(url, { method: 'GET' });
78
+ const init = await resp.json();
79
+ if (!init)
80
+ throw new TypeError(`Invalid response returned from url: ${url}`);
81
+ return await this.initDocument({ ...init, url });
82
+ }
83
+ async createBuiltinTypeDocument() {
84
+ const init = {
85
+ version: OpraSchema.SpecVersion,
86
+ info: {
87
+ version: OpraSchema.SpecVersion,
88
+ title: 'Opra built-in types',
89
+ contact: [{
90
+ url: 'https://github.com/oprajs/opra'
91
+ }
92
+ ],
93
+ license: {
94
+ url: 'https://github.com/oprajs/opra/blob/main/LICENSE',
95
+ name: 'MIT'
96
+ }
97
+ },
98
+ types: [AnyType, Base64Type, BigintType, BooleanType,
99
+ DateType, UuidType, IntegerType, NullType,
100
+ NumberType, ObjectType, ObjectIdType, StringType,
101
+ TimeType, TimestampType
102
+ ]
103
+ };
104
+ const factory = new TypeDocumentFactory();
105
+ factory.document = new TypeDocument();
106
+ return await factory.initDocument({ ...init, noBuiltinTypes: true });
107
+ }
108
+ async addReferences(references) {
109
+ const { document } = this;
110
+ for (const [ns, r] of Object.entries(references)) {
111
+ if (typeof r === 'string') {
112
+ document.references.set(ns, await this.initDocumentFromUrl(r));
113
+ }
114
+ else if (r instanceof TypeDocument)
115
+ document.references.set(ns, r);
116
+ else if (typeof r === 'object') {
117
+ document.references.set(ns, await this.initDocument(r));
118
+ }
119
+ else
120
+ throw new TypeError(`Invalid document reference (${ns}) in schema`);
121
+ }
122
+ }
123
+ async importDataType(thunk) {
124
+ thunk = await resolveThunk(thunk);
125
+ let name = '';
126
+ let schema;
127
+ let ctor;
128
+ if (typeof thunk === 'string') {
129
+ name = thunk;
130
+ schema = this.typeQueue.get(name);
131
+ }
132
+ else if (typeof thunk === 'function') {
133
+ const metadata = Reflect.getMetadata(DATATYPE_METADATA, thunk);
134
+ if (!metadata) {
135
+ // Check if is an internal type class like String, Number etc
136
+ const dataType = this.document.getDataType(thunk, true);
137
+ if (dataType)
138
+ return dataType;
139
+ throw new TypeError(`Class "${thunk.name}" doesn't have a valid DataType metadata`);
140
+ }
141
+ name = metadata.name;
142
+ schema = metadata;
143
+ ctor = thunk;
144
+ }
145
+ else if (typeof thunk === 'object') {
146
+ if (OpraSchema.isDataType(thunk)) {
147
+ name = thunk.name;
148
+ ctor = thunk.ctor || ctor;
149
+ schema = thunk;
150
+ }
151
+ else {
152
+ // It should be an enum object
153
+ const metadata = thunk[DATATYPE_METADATA];
154
+ if (!metadata)
155
+ throw new TypeError(`No EnumType metadata found for object ${JSON.stringify(thunk).substring(0, 20)}...`);
156
+ name = metadata.name;
157
+ const dataType = this.document.getDataType(name, true);
158
+ if (dataType)
159
+ return dataType;
160
+ schema = cloneObject(metadata);
161
+ }
162
+ }
163
+ ctor = ctor ?? (schema && (isConstructor(schema.ctor)) ? schema.ctor : undefined);
164
+ if (name) {
165
+ if (this.circularRefs.has(name.toLowerCase()))
166
+ throw new TypeError('Circular reference detected');
167
+ const dataType = this.document.getDataType(name, true);
168
+ if (dataType)
169
+ return dataType;
170
+ this.curPath.push('/' + name);
171
+ this.circularRefs.set(name, 1);
172
+ }
173
+ if (ctor) {
174
+ if (this.circularRefs.has(ctor))
175
+ throw new TypeError('Circular reference detected');
176
+ const dataType = this.document.getDataType(ctor, true);
177
+ if (dataType)
178
+ return dataType;
179
+ this.circularRefs.set(ctor, 1);
180
+ }
181
+ try {
182
+ if (!OpraSchema.isDataType(schema))
183
+ throw new TypeError(`No DataType schema determined`);
184
+ // Create an empty DataType instance and add in to document.
185
+ // This will help us for circular dependent data types
186
+ const instance = this.createDataTypeInstance(schema.kind, name);
187
+ if (name)
188
+ this.document.types.set(name, instance);
189
+ const initArguments = cloneObject(schema);
190
+ await this.prepareDataTypeInitArguments(initArguments, ctor);
191
+ if (initArguments.kind === 'ComplexType')
192
+ ComplexType.apply(instance, [this.document, initArguments]);
193
+ else if (initArguments.kind === 'SimpleType')
194
+ SimpleType.apply(instance, [this.document, initArguments]);
195
+ else if (initArguments.kind === 'EnumType')
196
+ EnumType.apply(instance, [this.document, initArguments]);
197
+ else if (initArguments.kind === 'MappedType')
198
+ MappedType.apply(instance, [this.document, initArguments]);
199
+ else if (initArguments.kind === 'UnionType')
200
+ UnionType.apply(instance, [this.document, initArguments]);
201
+ else
202
+ throw new TypeError(`Invalid data type schema: ${String(schema)}`);
203
+ return instance;
204
+ }
205
+ finally {
206
+ if (name) {
207
+ this.curPath.pop();
208
+ this.circularRefs.delete(name.toLowerCase());
209
+ }
210
+ if (ctor)
211
+ this.circularRefs.delete(ctor);
212
+ }
213
+ }
214
+ async prepareDataTypeInitArguments(schema, ctor) {
215
+ const initArguments = schema;
216
+ // Import extending class first
217
+ if (initArguments.kind === 'SimpleType' || initArguments.kind === 'ComplexType' ||
218
+ initArguments.kind === 'EnumType') {
219
+ if (ctor) {
220
+ const baseClass = Object.getPrototypeOf(ctor.prototype).constructor;
221
+ const baseMeta = Reflect.getMetadata(DATATYPE_METADATA, baseClass);
222
+ if (baseMeta) {
223
+ initArguments.base = await this.importDataType(baseClass);
224
+ }
225
+ }
226
+ else if (initArguments.base) {
227
+ initArguments.base = await this.importDataType(initArguments.base);
228
+ }
229
+ }
230
+ if (initArguments.kind === 'SimpleType' && ctor) {
231
+ if (typeof ctor.prototype.decode === 'function')
232
+ initArguments.decoder = initArguments.name
233
+ ? validator(initArguments.name, ctor.prototype.decode) : validator(ctor.prototype.decode);
234
+ if (typeof ctor.prototype.encode === 'function')
235
+ initArguments.decoder = initArguments.name
236
+ ? validator(initArguments.name, ctor.prototype.encode) : validator(ctor.prototype.encode);
237
+ return;
238
+ }
239
+ if (initArguments.kind === 'ComplexType') {
240
+ initArguments.ctor = ctor;
241
+ if (initArguments.fields) {
242
+ const srcFields = initArguments.fields;
243
+ const trgFields = initArguments.fields = {};
244
+ for (const [fieldName, o] of Object.entries(srcFields)) {
245
+ try {
246
+ this.curPath.push('.' + fieldName);
247
+ const srcMeta = typeof o === 'string' ? { type: o } : o;
248
+ const fieldInit = trgFields[fieldName] = {
249
+ ...srcMeta,
250
+ name: fieldName
251
+ };
252
+ if (srcMeta.enum) {
253
+ const enumObject = srcMeta.enum;
254
+ delete srcMeta.enum;
255
+ if (enumObject[DATATYPE_METADATA]) {
256
+ fieldInit.type = await this.importDataType(enumObject);
257
+ }
258
+ else {
259
+ const enumMeta = EnumType(enumObject);
260
+ fieldInit.type = await this.importDataType({ ...enumMeta, kind: 'EnumType', base: undefined });
261
+ }
262
+ }
263
+ else {
264
+ if (srcMeta.isArray && !srcMeta.type)
265
+ throw new TypeError(`"type" must be defined explicitly for array properties`);
266
+ fieldInit.type = await this.importDataType(srcMeta.type || srcMeta.designType || 'any');
267
+ }
268
+ this.curPath.pop();
269
+ }
270
+ catch (e) {
271
+ e.message = `Error in resource "${initArguments.name}.${fieldName}". ` + e.message;
272
+ throw e;
273
+ }
274
+ }
275
+ }
276
+ }
277
+ if (initArguments.kind === 'MappedType') {
278
+ const dataType = await this.importDataType(initArguments.base);
279
+ // istanbul ignore next
280
+ if (!(dataType instanceof ComplexType))
281
+ throw new TypeError('MappedType.type property must address to a ComplexType');
282
+ initArguments.base = dataType;
283
+ }
284
+ if (initArguments.kind === 'UnionType') {
285
+ const oldTypes = initArguments.types;
286
+ initArguments.types = [];
287
+ for (const type of oldTypes)
288
+ initArguments.types.push(await this.importDataType(type));
289
+ }
290
+ return initArguments;
291
+ }
292
+ createDataTypeInstance(kind, name) {
293
+ const dataType = {
294
+ document: this.document,
295
+ kind,
296
+ name
297
+ };
298
+ switch (kind) {
299
+ case OpraSchema.ComplexType.Kind:
300
+ Object.setPrototypeOf(dataType, ComplexType.prototype);
301
+ break;
302
+ case OpraSchema.EnumType.Kind:
303
+ Object.setPrototypeOf(dataType, EnumType.prototype);
304
+ break;
305
+ case OpraSchema.MappedType.Kind:
306
+ Object.setPrototypeOf(dataType, MappedType.prototype);
307
+ break;
308
+ case OpraSchema.SimpleType.Kind:
309
+ Object.setPrototypeOf(dataType, SimpleType.prototype);
310
+ break;
311
+ case OpraSchema.UnionType.Kind:
312
+ Object.setPrototypeOf(dataType, UnionType.prototype);
313
+ break;
314
+ default:
315
+ throw new TypeError(`Unknown DataType kind (${kind})`);
316
+ }
317
+ return dataType;
318
+ }
319
+ }
320
+ TypeDocumentFactory.designTypeMap = new Map();
@@ -1,7 +1,9 @@
1
1
  import 'reflect-metadata';
2
2
  export * from './constants.js';
3
3
  export * from './api-document.js';
4
- export * from './factory/index.js';
4
+ export * from './type-document.js';
5
+ export * from './factory/type-document-factory.js';
6
+ export * from './factory/api-document-factory.js';
5
7
  export * from './data-type/data-type.js';
6
8
  export * from './data-type/complex-type.js';
7
9
  export * from './data-type/field.js';
@@ -10,9 +12,13 @@ export * from './data-type/mapped-type.js';
10
12
  export * from './data-type/simple-type.js';
11
13
  export * from './data-type/union-type.js';
12
14
  export * from './resource/resource.js';
15
+ export * from './resource/crud-resource.js';
13
16
  export * from './resource/collection.js';
17
+ export * from './resource/container.js';
14
18
  export * from './resource/singleton.js';
15
19
  export * from './resource/storage.js';
20
+ export * from './resource/endpoint.js';
21
+ export * from './resource/parameter.js';
16
22
  export * from './interfaces/collection.interface.js';
17
23
  export * from './interfaces/singleton.interface.js';
18
24
  export * from './interfaces/storage.interface.js';
@@ -0,0 +1,23 @@
1
+ import { RESOURCE_METADATA } from '../constants.js';
2
+ export function createActionDecorator(options, bannedProperties, list) {
3
+ const decorator = ((target, propertyKey) => {
4
+ if (typeof propertyKey === 'string' && bannedProperties.includes(propertyKey))
5
+ throw new TypeError(`The "${propertyKey}" property is reserved for "${propertyKey}" operations and cannot be used as an action'`);
6
+ const resourceMetadata = (Reflect.getOwnMetadata(RESOURCE_METADATA, target.constructor) || {});
7
+ resourceMetadata.actions = resourceMetadata.actions || {};
8
+ const actionMeta = { ...options };
9
+ resourceMetadata.actions[propertyKey] = actionMeta;
10
+ for (const fn of list)
11
+ fn(actionMeta);
12
+ Reflect.defineMetadata(RESOURCE_METADATA, resourceMetadata, target.constructor);
13
+ });
14
+ decorator.Parameter = (name, arg0) => {
15
+ const parameterOptions = typeof arg0 === 'string' || typeof arg0 === 'function' ? { type: arg0 } : { ...arg0 };
16
+ list.push((operationMeta) => {
17
+ operationMeta.parameters = operationMeta.parameters || {};
18
+ operationMeta.parameters[name] = { ...parameterOptions };
19
+ });
20
+ return decorator;
21
+ };
22
+ return decorator;
23
+ }
@@ -1,20 +1,14 @@
1
1
  import * as vg from 'valgen';
2
2
  import { BadRequestError } from '../../exception/index.js';
3
3
  import { OpraFilter } from '../../filter/index.js';
4
- import { omitUndefined } from '../../helpers/object-utils.js';
5
4
  import { translate } from '../../i18n/index.js';
6
5
  import { OpraSchema } from '../../schema/index.js';
7
6
  import { SimpleType } from '../data-type/simple-type.js';
8
- import { generateCodec } from '../utils/generate-codec.js';
9
- import { Resource } from './resource.js';
10
- export class CollectionClass extends Resource {
11
- constructor(document, init) {
12
- super(document, init);
13
- this._decoders = {};
14
- this._encoders = {};
7
+ import { CrudResource } from './crud-resource.js';
8
+ export class CollectionClass extends CrudResource {
9
+ constructor(parent, init) {
10
+ super(parent, init);
15
11
  this.kind = OpraSchema.Collection.Kind;
16
- this.controller = init.controller;
17
- this.operations = { ...init.operations };
18
12
  const dataType = this.type = init.type;
19
13
  // Validate key fields
20
14
  this.primaryKey = init.primaryKey
@@ -27,15 +21,98 @@ export class CollectionClass extends Resource {
27
21
  if (!(field?.type instanceof SimpleType))
28
22
  throw new TypeError(`Only Simple type allowed for primary keys but "${f}" is a ${field.type.kind}`);
29
23
  });
24
+ // ------------------
25
+ let endpoint = this.operations.get('create');
26
+ if (endpoint) {
27
+ endpoint.returnType = this.type;
28
+ endpoint.decode = this.type.generateCodec('decode', {
29
+ partial: true,
30
+ pick: endpoint.inputPickFields,
31
+ omit: endpoint.inputOmitFields,
32
+ });
33
+ endpoint.encode = this.type.generateCodec('encode', {
34
+ partial: true,
35
+ pick: endpoint.outputPickFields,
36
+ omit: endpoint.outputOmitFields,
37
+ });
38
+ endpoint.defineParameter('pick', { type: 'string', isArray: true, isBuiltin: true });
39
+ endpoint.defineParameter('omit', { type: 'string', isArray: true, isBuiltin: true });
40
+ endpoint.defineParameter('include', { type: 'string', isArray: true, isBuiltin: true });
41
+ }
42
+ // ------------------
43
+ endpoint = this.operations.get('deleteMany');
44
+ if (endpoint) {
45
+ endpoint.defineParameter('filter', { type: 'string', isBuiltin: true });
46
+ }
47
+ // ------------------
48
+ endpoint = this.operations.get('get');
49
+ if (endpoint) {
50
+ endpoint.returnType = this.type;
51
+ endpoint.encode = this.type.generateCodec('encode', {
52
+ partial: true,
53
+ pick: endpoint.outputPickFields,
54
+ omit: endpoint.outputOmitFields,
55
+ });
56
+ endpoint.defineParameter('pick', { type: 'string', isArray: true, isBuiltin: true });
57
+ endpoint.defineParameter('omit', { type: 'string', isArray: true, isBuiltin: true });
58
+ endpoint.defineParameter('include', { type: 'string', isArray: true, isBuiltin: true });
59
+ }
60
+ // ------------------
61
+ endpoint = this.operations.get('findMany');
62
+ if (endpoint) {
63
+ endpoint.returnType = this.type;
64
+ endpoint.encode = vg.isArray(this.type.generateCodec('encode', {
65
+ partial: true,
66
+ pick: endpoint.outputPickFields,
67
+ omit: endpoint.outputOmitFields,
68
+ }));
69
+ endpoint.defineParameter('pick', { type: 'string', isArray: true, isBuiltin: true });
70
+ endpoint.defineParameter('omit', { type: 'string', isArray: true, isBuiltin: true });
71
+ endpoint.defineParameter('include', { type: 'string', isArray: true, isBuiltin: true });
72
+ endpoint.defineParameter('sort', { type: 'string', isArray: true, isBuiltin: true });
73
+ endpoint.defineParameter('filter', { type: 'string', isBuiltin: true });
74
+ endpoint.defineParameter('limit', { type: 'integer', isBuiltin: true });
75
+ endpoint.defineParameter('skip', { type: 'integer', isBuiltin: true });
76
+ endpoint.defineParameter('distinct', { type: 'boolean', isBuiltin: true });
77
+ endpoint.defineParameter('count', { type: 'boolean', isBuiltin: true });
78
+ }
79
+ // ------------------
80
+ endpoint = this.operations.get('update');
81
+ if (endpoint) {
82
+ endpoint.returnType = this.type;
83
+ endpoint.decode = this.type.generateCodec('decode', {
84
+ pick: endpoint.inputPickFields,
85
+ omit: endpoint.inputOmitFields,
86
+ });
87
+ endpoint.encode = this.type.generateCodec('encode', {
88
+ partial: true,
89
+ pick: endpoint.outputPickFields,
90
+ omit: endpoint.outputOmitFields,
91
+ });
92
+ endpoint.defineParameter('pick', { type: 'string', isArray: true, isBuiltin: true });
93
+ endpoint.defineParameter('omit', { type: 'string', isArray: true, isBuiltin: true });
94
+ endpoint.defineParameter('include', { type: 'string', isArray: true, isBuiltin: true });
95
+ }
96
+ // ------------------
97
+ endpoint = this.operations.get('updateMany');
98
+ if (endpoint) {
99
+ endpoint.decode = this.type.generateCodec('decode', {
100
+ pick: endpoint.inputPickFields,
101
+ omit: endpoint.inputOmitFields,
102
+ });
103
+ endpoint.defineParameter('filter', { type: 'string', isBuiltin: true });
104
+ }
30
105
  }
31
- exportSchema() {
106
+ getOperation(name) {
107
+ return super.getOperation(name);
108
+ }
109
+ exportSchema(options) {
32
110
  return {
33
- ...super.exportSchema(),
34
- ...omitUndefined({
35
- type: this.type.name || 'object',
36
- operations: this.operations,
111
+ ...super.exportSchema(options),
112
+ type: this.type.name || 'object',
113
+ ...{
37
114
  primaryKey: this.primaryKey
38
- })
115
+ }
39
116
  };
40
117
  }
41
118
  parseKeyValue(value) {
@@ -79,7 +156,7 @@ export class CollectionClass extends Resource {
79
156
  const normalized = this.type.normalizeFieldPath(fields);
80
157
  if (!normalized)
81
158
  return;
82
- const findManyOp = this.operations.findMany;
159
+ const findManyOp = this.getOperation('findMany');
83
160
  const sortFields = findManyOp && findManyOp.sortFields;
84
161
  (Array.isArray(normalized) ? normalized : [normalized]).forEach(field => {
85
162
  if (!sortFields?.find(x => x === field))
@@ -98,7 +175,7 @@ export class CollectionClass extends Resource {
98
175
  if (!(ast.left instanceof OpraFilter.QualifiedIdentifier && ast.left.field))
99
176
  throw new TypeError(`Invalid filter query. Left side should be a data field.`);
100
177
  // Check if filtering accepted for given field
101
- const findManyOp = this.operations.findMany;
178
+ const findManyOp = this.getOperation('findMany');
102
179
  const fieldLower = ast.left.value.toLowerCase();
103
180
  const filterDef = (findManyOp && findManyOp.filters || [])
104
181
  .find(f => f.field.toLowerCase() === fieldLower);
@@ -140,30 +217,4 @@ export class CollectionClass extends Resource {
140
217
  }
141
218
  return ast;
142
219
  }
143
- getDecoder(endpoint) {
144
- let decoder = this._decoders[endpoint];
145
- if (decoder)
146
- return decoder;
147
- const options = {
148
- partial: endpoint !== 'create'
149
- };
150
- if (endpoint !== 'create')
151
- options.omit = [...this.primaryKey];
152
- decoder = generateCodec(this.type, 'decode', options);
153
- this._decoders[endpoint] = decoder;
154
- return decoder;
155
- }
156
- getEncoder(endpoint) {
157
- let encoder = this._encoders[endpoint];
158
- if (encoder)
159
- return encoder;
160
- const options = {
161
- partial: true
162
- };
163
- encoder = generateCodec(this.type, 'encode', options);
164
- if (endpoint === 'findMany')
165
- return vg.isArray(encoder);
166
- this._encoders[endpoint] = encoder;
167
- return encoder;
168
- }
169
220
  }