@opra/common 0.25.4 → 0.26.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.
- package/browser.js +1843 -1355
- package/cjs/document/api-document.js +16 -220
- package/cjs/document/data-type/complex-type-class.js +41 -19
- package/cjs/document/data-type/complex-type.js +1 -1
- package/cjs/document/data-type/data-type.js +2 -1
- package/cjs/document/data-type/enum-type-class.js +15 -12
- package/cjs/document/data-type/enum-type.js +28 -14
- package/cjs/document/data-type/field-class.js +21 -3
- package/cjs/document/data-type/field-decorator.js +0 -19
- package/cjs/document/data-type/mapped-type-class.js +8 -10
- package/cjs/document/data-type/mapped-type.js +1 -1
- package/cjs/document/data-type/simple-type-class.js +17 -8
- package/cjs/document/data-type/simple-type.js +1 -1
- package/cjs/document/data-type/union-type-class.js +15 -13
- package/cjs/document/document-base.js +24 -0
- package/cjs/document/factory/api-document-factory.js +231 -0
- package/cjs/document/factory/type-document-factory.js +324 -0
- package/cjs/document/index.js +7 -1
- package/cjs/document/resource/action-decorator.js +27 -0
- package/cjs/document/resource/collection-class.js +95 -44
- package/cjs/document/resource/collection-decorator.js +182 -0
- package/cjs/document/resource/collection.js +4 -4
- package/cjs/document/resource/container-class.js +82 -0
- package/cjs/document/resource/container-decorator.js +24 -0
- package/cjs/document/resource/container.js +29 -0
- package/cjs/document/resource/crud-resource.js +31 -0
- package/cjs/document/resource/endpoint.js +28 -21
- package/cjs/document/resource/operation-decorator.js +27 -0
- package/cjs/document/resource/parameter.js +51 -0
- package/cjs/document/{decorators/resource.decorator.js → resource/resource-decorator.js} +9 -14
- package/cjs/document/resource/resource.js +20 -5
- package/cjs/document/resource/singleton-class.js +60 -40
- package/cjs/document/resource/singleton-decorator.js +57 -0
- package/cjs/document/resource/singleton.js +4 -4
- package/cjs/document/resource/storage-class.js +11 -17
- package/cjs/document/resource/storage-decorator.js +66 -0
- package/cjs/document/resource/storage.js +3 -3
- package/cjs/document/type-document.js +195 -0
- package/cjs/exception/http-errors/internal-server.error.js +1 -0
- package/cjs/exception/opra-exception.js +18 -12
- package/cjs/exception/wrap-exception.js +2 -1
- package/cjs/helpers/responsive-map.js +3 -3
- package/cjs/http/opra-url-path.js +5 -1
- package/cjs/schema/document/document-base.interface.js +2 -0
- package/cjs/schema/document/type-document.interface.js +2 -0
- package/cjs/schema/opra-schema.ns.js +3 -1
- package/cjs/schema/type-guards.js +3 -3
- package/esm/document/api-document.js +16 -220
- package/esm/document/data-type/complex-type-class.js +40 -19
- package/esm/document/data-type/complex-type.js +1 -1
- package/esm/document/data-type/data-type.js +2 -1
- package/esm/document/data-type/enum-type-class.js +15 -12
- package/esm/document/data-type/enum-type.js +26 -12
- package/esm/document/data-type/field-class.js +20 -3
- package/esm/document/data-type/field-decorator.js +0 -19
- package/esm/document/data-type/mapped-type-class.js +9 -11
- package/esm/document/data-type/mapped-type.js +1 -1
- package/esm/document/data-type/simple-type-class.js +15 -6
- package/esm/document/data-type/simple-type.js +1 -1
- package/esm/document/data-type/union-type-class.js +11 -9
- package/esm/document/document-base.js +20 -0
- package/esm/document/factory/api-document-factory.js +227 -0
- package/esm/document/factory/type-document-factory.js +320 -0
- package/esm/document/index.js +7 -1
- package/esm/document/resource/action-decorator.js +23 -0
- package/esm/document/resource/collection-class.js +95 -44
- package/esm/document/resource/collection-decorator.js +178 -0
- package/esm/document/resource/collection.js +4 -4
- package/esm/document/resource/container-class.js +78 -0
- package/esm/document/resource/container-decorator.js +20 -0
- package/esm/document/resource/container.js +25 -0
- package/esm/document/resource/crud-resource.js +27 -0
- package/esm/document/resource/endpoint.js +27 -21
- package/esm/document/resource/operation-decorator.js +23 -0
- package/esm/document/resource/parameter.js +46 -0
- package/esm/document/resource/resource-decorator.js +28 -0
- package/esm/document/resource/resource.js +20 -5
- package/esm/document/resource/singleton-class.js +58 -38
- package/esm/document/resource/singleton-decorator.js +53 -0
- package/esm/document/resource/singleton.js +4 -4
- package/esm/document/resource/storage-class.js +9 -15
- package/esm/document/resource/storage-decorator.js +62 -0
- package/esm/document/resource/storage.js +3 -3
- package/esm/document/type-document.js +191 -0
- package/esm/exception/http-errors/internal-server.error.js +1 -0
- package/esm/exception/opra-exception.js +18 -12
- package/esm/exception/wrap-exception.js +2 -1
- package/esm/helpers/responsive-map.js +3 -3
- package/esm/http/opra-url-path.js +4 -1
- package/esm/schema/document/document-base.interface.js +1 -0
- package/esm/schema/document/type-document.interface.js +1 -0
- package/esm/schema/opra-schema.ns.js +3 -1
- package/esm/schema/type-guards.js +1 -1
- package/package.json +2 -2
- package/types/document/api-document.d.ts +32 -73
- package/types/document/data-type/complex-type-class.d.ts +5 -5
- package/types/document/data-type/complex-type.d.ts +8 -3
- package/types/document/{decorators → data-type}/complex-type.decorator.d.ts +1 -1
- package/types/document/data-type/data-type.d.ts +11 -1
- package/types/document/data-type/enum-type-class.d.ts +6 -6
- package/types/document/data-type/enum-type.d.ts +12 -6
- package/types/document/data-type/field-class.d.ts +9 -1
- package/types/document/data-type/field-decorator.d.ts +1 -1
- package/types/document/data-type/field.d.ts +4 -3
- package/types/document/data-type/mapped-type-class.d.ts +5 -9
- package/types/document/data-type/mapped-type.d.ts +6 -7
- package/types/document/data-type/simple-type-class.d.ts +6 -3
- package/types/document/{decorators → data-type}/simple-type.decorator.d.ts +1 -1
- package/types/document/data-type/union-type-class.d.ts +2 -8
- package/types/document/data-type/union-type.d.ts +7 -6
- package/types/document/document-base.d.ts +10 -0
- package/types/document/factory/api-document-factory.d.ts +50 -0
- package/types/document/factory/type-document-factory.d.ts +56 -0
- package/types/document/index.d.ts +7 -1
- package/types/document/resource/action-decorator.d.ts +6 -0
- package/types/document/resource/collection-class.d.ts +17 -12
- package/types/document/resource/collection-decorator.d.ts +192 -0
- package/types/document/resource/collection.d.ts +11 -19
- package/types/document/resource/container-class.d.ts +66 -0
- package/types/document/resource/container-decorator.d.ts +37 -0
- package/types/document/resource/container.d.ts +25 -0
- package/types/document/resource/crud-resource.d.ts +16 -0
- package/types/document/resource/endpoint.d.ts +26 -19
- package/types/document/resource/operation-decorator.d.ts +6 -0
- package/types/document/resource/parameter.d.ts +35 -0
- package/types/document/resource/resource-decorator.d.ts +32 -0
- package/types/document/resource/resource.d.ts +15 -13
- package/types/document/resource/singleton-class.d.ts +15 -14
- package/types/document/resource/singleton-decorator.d.ts +125 -0
- package/types/document/resource/singleton.d.ts +11 -16
- package/types/document/resource/storage-class.d.ts +13 -6
- package/types/document/resource/storage-decorator.d.ts +98 -0
- package/types/document/resource/storage.d.ts +9 -12
- package/types/document/type-document.d.ts +68 -0
- package/types/exception/opra-exception.d.ts +1 -1
- package/types/http/opra-url-path.d.ts +2 -1
- package/types/schema/data-type/complex-type.interface.d.ts +1 -1
- package/types/schema/data-type/data-type.interface.d.ts +3 -3
- package/types/schema/data-type/enum-type.interface.d.ts +6 -7
- package/types/schema/data-type/mapped-type.interface.d.ts +4 -3
- package/types/schema/data-type/simple-type.interface.d.ts +3 -3
- package/types/schema/data-type/union-type.interface.d.ts +6 -4
- package/types/schema/document/api-document.interface.d.ts +11 -0
- package/types/schema/document/document-base.interface.d.ts +24 -0
- package/types/schema/document/type-document.interface.d.ts +6 -0
- package/types/schema/opra-schema.ns.d.ts +3 -1
- package/types/schema/resource/collection.interface.d.ts +31 -31
- package/types/schema/resource/container.interface.d.ts +4 -3
- package/types/schema/resource/resource.interface.d.ts +1 -1
- package/types/schema/resource/singleton.interface.d.ts +15 -10
- package/types/schema/resource/storage.interface.d.ts +49 -45
- package/types/schema/type-guards.d.ts +3 -2
- package/cjs/document/decorators/collection-decorator.js +0 -38
- package/cjs/document/decorators/singleton.decorator.js +0 -36
- package/cjs/document/decorators/storage.decorator.js +0 -35
- package/cjs/document/factory/add-references.js +0 -20
- package/cjs/document/factory/create-document.js +0 -83
- package/cjs/document/factory/factory.js +0 -66
- package/cjs/document/factory/import-resource-class.js +0 -54
- package/cjs/document/factory/import-type-class.js +0 -146
- package/cjs/document/factory/index.js +0 -4
- package/cjs/document/factory/process-resources.js +0 -70
- package/cjs/document/factory/process-types.js +0 -191
- package/cjs/document/utils/generate-codec.js +0 -39
- package/esm/document/decorators/collection-decorator.js +0 -34
- package/esm/document/decorators/resource.decorator.js +0 -33
- package/esm/document/decorators/singleton.decorator.js +0 -32
- package/esm/document/decorators/storage.decorator.js +0 -31
- package/esm/document/factory/add-references.js +0 -16
- package/esm/document/factory/create-document.js +0 -77
- package/esm/document/factory/factory.js +0 -62
- package/esm/document/factory/import-resource-class.js +0 -48
- package/esm/document/factory/import-type-class.js +0 -136
- package/esm/document/factory/index.js +0 -1
- package/esm/document/factory/process-resources.js +0 -63
- package/esm/document/factory/process-types.js +0 -185
- package/esm/document/utils/generate-codec.js +0 -33
- package/types/document/decorators/collection-decorator.d.ts +0 -30
- package/types/document/decorators/resource.decorator.d.ts +0 -9
- package/types/document/decorators/singleton.decorator.d.ts +0 -25
- package/types/document/decorators/storage.decorator.d.ts +0 -22
- package/types/document/factory/add-references.d.ts +0 -4
- package/types/document/factory/create-document.d.ts +0 -12
- package/types/document/factory/factory.d.ts +0 -63
- package/types/document/factory/import-resource-class.d.ts +0 -10
- package/types/document/factory/import-type-class.d.ts +0 -17
- package/types/document/factory/index.d.ts +0 -1
- package/types/document/factory/process-resources.d.ts +0 -9
- package/types/document/factory/process-types.d.ts +0 -6
- package/types/document/utils/generate-codec.d.ts +0 -10
- package/types/schema/document.interface.d.ts +0 -34
- /package/cjs/document/{decorators → data-type}/complex-type.decorator.js +0 -0
- /package/cjs/document/{decorators → data-type}/simple-type.decorator.js +0 -0
- /package/cjs/schema/{document.interface.js → document/api-document.interface.js} +0 -0
- /package/esm/document/{decorators → data-type}/complex-type.decorator.js +0 -0
- /package/esm/document/{decorators → data-type}/simple-type.decorator.js +0 -0
- /package/esm/schema/{document.interface.js → document/api-document.interface.js} +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { cloneObject, resolveThunk, ResponsiveMap } from '../../helpers/index.js';
|
|
2
|
+
import { OpraSchema } from '../../schema/index.js';
|
|
3
|
+
import { ApiDocument } from '../api-document.js';
|
|
4
|
+
import { RESOURCE_METADATA } from '../constants.js';
|
|
5
|
+
import { ComplexType } from '../data-type/complex-type.js';
|
|
6
|
+
import { EnumType } from '../data-type/enum-type.js';
|
|
7
|
+
import { Collection } from '../resource/collection.js';
|
|
8
|
+
import { Container } from '../resource/container.js';
|
|
9
|
+
import { Singleton } from '../resource/singleton.js';
|
|
10
|
+
import { Storage } from '../resource/storage.js';
|
|
11
|
+
import { TypeDocumentFactory } from './type-document-factory.js';
|
|
12
|
+
/**
|
|
13
|
+
* @class ApiDocumentFactory
|
|
14
|
+
*/
|
|
15
|
+
export class ApiDocumentFactory extends TypeDocumentFactory {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.resourceQueue = new ResponsiveMap();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates ApiDocument instance from given schema object
|
|
22
|
+
* @param init
|
|
23
|
+
*/
|
|
24
|
+
static async createDocument(init) {
|
|
25
|
+
const factory = new ApiDocumentFactory();
|
|
26
|
+
const document = factory.document = new ApiDocument();
|
|
27
|
+
await factory.initDocument(init);
|
|
28
|
+
return document;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Downloads schema from the given URL and creates the document instance * @param url
|
|
32
|
+
*/
|
|
33
|
+
static async createDocumentFromUrl(url) {
|
|
34
|
+
const factory = new ApiDocumentFactory();
|
|
35
|
+
const document = factory.document = new ApiDocument();
|
|
36
|
+
await factory.initDocumentFromUrl(url);
|
|
37
|
+
return document;
|
|
38
|
+
}
|
|
39
|
+
async initDocument(init) {
|
|
40
|
+
await super.initDocument(init);
|
|
41
|
+
const processContainer = async (container, containerInit) => {
|
|
42
|
+
if (!containerInit.resources)
|
|
43
|
+
return;
|
|
44
|
+
if (Array.isArray(containerInit.resources)) {
|
|
45
|
+
for (const thunk of containerInit.resources) {
|
|
46
|
+
const initArguments = await this.importResourceInstance(thunk);
|
|
47
|
+
container.resources.set(initArguments.name, await this.createResource(container, initArguments));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
for (const [name, schema] of Object.entries(containerInit.resources)) {
|
|
52
|
+
const initArguments = await this.importResourceSchema(name, schema);
|
|
53
|
+
container.resources.set(initArguments.name, await this.createResource(container, initArguments));
|
|
54
|
+
}
|
|
55
|
+
container.resources.sort();
|
|
56
|
+
};
|
|
57
|
+
if (init.root) {
|
|
58
|
+
this.curPath.push('/root');
|
|
59
|
+
await processContainer(this.document.root, init.root);
|
|
60
|
+
this.curPath.pop();
|
|
61
|
+
this.document.invalidate();
|
|
62
|
+
}
|
|
63
|
+
return this.document;
|
|
64
|
+
}
|
|
65
|
+
async importResourceSchema(name, schema) {
|
|
66
|
+
const convertEndpoints = async (source) => {
|
|
67
|
+
if (!source)
|
|
68
|
+
return;
|
|
69
|
+
const output = {};
|
|
70
|
+
for (const [kA, oA] of Object.entries(source)) {
|
|
71
|
+
/* istanbul ignore next */
|
|
72
|
+
if (!oA)
|
|
73
|
+
continue;
|
|
74
|
+
let parameters;
|
|
75
|
+
if (oA.parameters) {
|
|
76
|
+
parameters = {};
|
|
77
|
+
for (const [kP, oP] of Object.entries(oA.parameters)) {
|
|
78
|
+
if (oP.enum) {
|
|
79
|
+
oP.type = EnumType(oP.enum, { name: kP + 'Enum' });
|
|
80
|
+
}
|
|
81
|
+
parameters[kP] = {
|
|
82
|
+
...oP,
|
|
83
|
+
type: await this.importDataType(oP.type || 'any')
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
output[kA] = { ...oA[kA], parameters };
|
|
88
|
+
}
|
|
89
|
+
return output;
|
|
90
|
+
};
|
|
91
|
+
if (schema.kind === 'Collection') {
|
|
92
|
+
return {
|
|
93
|
+
...schema,
|
|
94
|
+
kind: schema.kind,
|
|
95
|
+
name,
|
|
96
|
+
type: await this.importDataType(schema.type),
|
|
97
|
+
actions: await convertEndpoints(schema.actions),
|
|
98
|
+
operations: await convertEndpoints(schema.operations),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
else if (schema.kind === 'Singleton') {
|
|
102
|
+
return {
|
|
103
|
+
...schema,
|
|
104
|
+
kind: schema.kind,
|
|
105
|
+
name,
|
|
106
|
+
type: await this.importDataType(schema.type),
|
|
107
|
+
actions: await convertEndpoints(schema.actions),
|
|
108
|
+
operations: await convertEndpoints(schema.operations),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else if (schema.kind === 'Storage') {
|
|
112
|
+
return {
|
|
113
|
+
...schema,
|
|
114
|
+
name,
|
|
115
|
+
actions: await convertEndpoints(schema.actions),
|
|
116
|
+
operations: await convertEndpoints(schema.operations),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
else if (schema.kind === 'Container') {
|
|
120
|
+
const resources = [];
|
|
121
|
+
if (schema.resources) {
|
|
122
|
+
for (const [k, o] of Object.entries(schema.resources)) {
|
|
123
|
+
const rinit = await this.importResourceSchema(k, o);
|
|
124
|
+
resources.push(rinit);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
...schema,
|
|
129
|
+
name,
|
|
130
|
+
resources,
|
|
131
|
+
actions: await convertEndpoints(schema.actions)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
throw new TypeError(`Can not import resource schema (${schema.kind})`);
|
|
135
|
+
}
|
|
136
|
+
async importResourceInstance(thunk) {
|
|
137
|
+
thunk = await resolveThunk(thunk);
|
|
138
|
+
let ctor;
|
|
139
|
+
let metadata;
|
|
140
|
+
let instance;
|
|
141
|
+
if (typeof thunk === 'function') {
|
|
142
|
+
ctor = thunk;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
ctor = Object.getPrototypeOf(thunk).constructor;
|
|
146
|
+
instance = thunk;
|
|
147
|
+
if (!Reflect.hasMetadata(RESOURCE_METADATA, ctor) &&
|
|
148
|
+
OpraSchema.isResource(thunk) && typeof thunk.controller === 'object') {
|
|
149
|
+
ctor = Object.getPrototypeOf(thunk.controller).constructor;
|
|
150
|
+
metadata = thunk;
|
|
151
|
+
instance = thunk.controller;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
metadata = metadata || Reflect.getMetadata(RESOURCE_METADATA, ctor);
|
|
155
|
+
if (!metadata && OpraSchema.isResource(metadata))
|
|
156
|
+
throw new TypeError(`Class "${ctor.name}" doesn't have a valid Resource metadata`);
|
|
157
|
+
const convertEndpoints = async (source) => {
|
|
158
|
+
if (!source)
|
|
159
|
+
return;
|
|
160
|
+
const output = {};
|
|
161
|
+
for (const [kA, oA] of Object.entries(source)) {
|
|
162
|
+
let parameters;
|
|
163
|
+
if (oA.parameters) {
|
|
164
|
+
parameters = {};
|
|
165
|
+
for (const [kP, oP] of Object.entries(oA.parameters)) {
|
|
166
|
+
if (oP.enum) {
|
|
167
|
+
oP.type = EnumType(oP.enum, { name: kP + 'Enum' });
|
|
168
|
+
}
|
|
169
|
+
parameters[kP] = {
|
|
170
|
+
...oP,
|
|
171
|
+
type: await this.importDataType(oP.type || 'any')
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
output[kA] = { ...oA, parameters };
|
|
176
|
+
}
|
|
177
|
+
return output;
|
|
178
|
+
};
|
|
179
|
+
// Clone metadata to prevent changing its contents
|
|
180
|
+
const initArguments = cloneObject(metadata);
|
|
181
|
+
initArguments.controller = instance;
|
|
182
|
+
initArguments.ctor = ctor;
|
|
183
|
+
if (initArguments.actions)
|
|
184
|
+
initArguments.actions = await convertEndpoints(initArguments.actions);
|
|
185
|
+
if (initArguments.kind === 'Collection' || initArguments.kind === 'Singleton') {
|
|
186
|
+
const dataType = await this.importDataType((metadata).type);
|
|
187
|
+
if (!dataType)
|
|
188
|
+
throw new TypeError(`Unable to determine data type of "${initArguments.name}" resource`);
|
|
189
|
+
if (!(dataType instanceof ComplexType))
|
|
190
|
+
throw new TypeError(`Data type of "${initArguments.name}" resource is not a ComplexType`);
|
|
191
|
+
initArguments.type = dataType;
|
|
192
|
+
if (initArguments.operations)
|
|
193
|
+
initArguments.operations = await convertEndpoints(initArguments.operations);
|
|
194
|
+
}
|
|
195
|
+
else if (initArguments.kind === 'Container') {
|
|
196
|
+
const oldResources = initArguments.resources;
|
|
197
|
+
if (Array.isArray(oldResources)) {
|
|
198
|
+
initArguments.resources = [];
|
|
199
|
+
for (const t of oldResources) {
|
|
200
|
+
const rinit = await this.importResourceInstance(t);
|
|
201
|
+
initArguments.resources.push(rinit);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return initArguments;
|
|
206
|
+
}
|
|
207
|
+
async createResource(container, initArguments) {
|
|
208
|
+
if (initArguments.kind === 'Collection')
|
|
209
|
+
return new Collection(container, initArguments);
|
|
210
|
+
if (initArguments.kind === 'Singleton')
|
|
211
|
+
return new Singleton(container, initArguments);
|
|
212
|
+
if (initArguments.kind === 'Storage')
|
|
213
|
+
return new Storage(container, initArguments);
|
|
214
|
+
if (initArguments.kind === 'Container') {
|
|
215
|
+
const newContainer = new Container(container, { ...initArguments, resources: undefined });
|
|
216
|
+
if (initArguments.resources) {
|
|
217
|
+
for (const r of initArguments.resources) {
|
|
218
|
+
const res = await this.createResource(newContainer, r);
|
|
219
|
+
newContainer.resources.set(res.name, res);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return newContainer;
|
|
223
|
+
}
|
|
224
|
+
else
|
|
225
|
+
throw new Error(`Unknown resource type ${initArguments.kind}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -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();
|
package/esm/document/index.js
CHANGED
|
@@ -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 './
|
|
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
|
+
}
|