@opra/core 0.0.5
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/LICENSE +21 -0
- package/README.md +3 -0
- package/cjs/constants.js +5 -0
- package/cjs/decorators/entity-resource.decorator.js +24 -0
- package/cjs/enums/http-headers.enum.js +393 -0
- package/cjs/enums/http-status.enum.js +300 -0
- package/cjs/enums/index.js +5 -0
- package/cjs/enums/issue-severity.enum.js +2 -0
- package/cjs/exception/api-exception.js +63 -0
- package/cjs/exception/errors/bad-request.error.js +22 -0
- package/cjs/exception/errors/failed-dependency.error.js +21 -0
- package/cjs/exception/errors/forbidden.error.js +23 -0
- package/cjs/exception/errors/internal-server.error.js +21 -0
- package/cjs/exception/errors/method-not-allowed.error.js +22 -0
- package/cjs/exception/errors/not-found.error.js +25 -0
- package/cjs/exception/errors/unauthorized.error.js +22 -0
- package/cjs/exception/errors/unprocessable-entity.error.js +21 -0
- package/cjs/exception/index.js +11 -0
- package/cjs/implementation/adapter/adapter.js +72 -0
- package/cjs/implementation/adapter/express-adapter.js +93 -0
- package/cjs/implementation/adapter/http-adapter.js +262 -0
- package/cjs/implementation/data-type/complex-type.js +39 -0
- package/cjs/implementation/data-type/data-type.js +35 -0
- package/cjs/implementation/data-type/entity-type.js +33 -0
- package/cjs/implementation/data-type/simple-type.js +30 -0
- package/cjs/implementation/execution-context.js +49 -0
- package/cjs/implementation/opra-document.js +116 -0
- package/cjs/implementation/opra-service.js +59 -0
- package/cjs/implementation/resource/container-resource-controller.js +26 -0
- package/cjs/implementation/resource/entity-resource-info.js +68 -0
- package/cjs/implementation/resource/resource-info.js +24 -0
- package/cjs/implementation/schema-generator.js +173 -0
- package/cjs/index.js +25 -0
- package/cjs/interfaces/entity-resource.interface.js +2 -0
- package/cjs/interfaces/execution-query.interface.js +190 -0
- package/cjs/interfaces/http-context.interface.js +2 -0
- package/cjs/interfaces/opra-schema.metadata.js +2 -0
- package/cjs/interfaces/resource-container.interface.js +2 -0
- package/cjs/package.json +3 -0
- package/cjs/services/data-service.js +9 -0
- package/cjs/services/json-data-service.js +15 -0
- package/cjs/types.js +2 -0
- package/cjs/utils/class-utils.js +37 -0
- package/cjs/utils/headers.js +58 -0
- package/cjs/utils/internal-data-types.js +44 -0
- package/cjs/utils/responsive-object.js +49 -0
- package/cjs/utils/string-path-to-object-tree.js +26 -0
- package/cjs/utils/terminal-utils.js +7 -0
- package/esm/constants.d.ts +2 -0
- package/esm/constants.js +2 -0
- package/esm/decorators/entity-resource.decorator.d.ts +5 -0
- package/esm/decorators/entity-resource.decorator.js +19 -0
- package/esm/enums/http-headers.enum.d.ts +368 -0
- package/esm/enums/http-headers.enum.js +390 -0
- package/esm/enums/http-status.enum.d.ts +290 -0
- package/esm/enums/http-status.enum.js +297 -0
- package/esm/enums/index.d.ts +2 -0
- package/esm/enums/index.js +2 -0
- package/esm/enums/issue-severity.enum.d.ts +1 -0
- package/esm/enums/issue-severity.enum.js +1 -0
- package/esm/exception/api-exception.d.ts +38 -0
- package/esm/exception/api-exception.js +59 -0
- package/esm/exception/errors/bad-request.error.d.ts +9 -0
- package/esm/exception/errors/bad-request.error.js +18 -0
- package/esm/exception/errors/failed-dependency.error.d.ts +8 -0
- package/esm/exception/errors/failed-dependency.error.js +17 -0
- package/esm/exception/errors/forbidden.error.d.ts +10 -0
- package/esm/exception/errors/forbidden.error.js +19 -0
- package/esm/exception/errors/internal-server.error.d.ts +8 -0
- package/esm/exception/errors/internal-server.error.js +17 -0
- package/esm/exception/errors/method-not-allowed.error.d.ts +9 -0
- package/esm/exception/errors/method-not-allowed.error.js +18 -0
- package/esm/exception/errors/not-found.error.d.ts +12 -0
- package/esm/exception/errors/not-found.error.js +21 -0
- package/esm/exception/errors/unauthorized.error.d.ts +9 -0
- package/esm/exception/errors/unauthorized.error.js +18 -0
- package/esm/exception/errors/unprocessable-entity.error.d.ts +8 -0
- package/esm/exception/errors/unprocessable-entity.error.js +17 -0
- package/esm/exception/index.d.ts +8 -0
- package/esm/exception/index.js +8 -0
- package/esm/implementation/adapter/adapter.d.ts +18 -0
- package/esm/implementation/adapter/adapter.js +68 -0
- package/esm/implementation/adapter/express-adapter.d.ts +12 -0
- package/esm/implementation/adapter/express-adapter.js +89 -0
- package/esm/implementation/adapter/http-adapter.d.ts +27 -0
- package/esm/implementation/adapter/http-adapter.js +258 -0
- package/esm/implementation/data-type/complex-type.d.ts +18 -0
- package/esm/implementation/data-type/complex-type.js +35 -0
- package/esm/implementation/data-type/data-type.d.ts +15 -0
- package/esm/implementation/data-type/data-type.js +31 -0
- package/esm/implementation/data-type/entity-type.d.ts +10 -0
- package/esm/implementation/data-type/entity-type.js +29 -0
- package/esm/implementation/data-type/simple-type.d.ts +15 -0
- package/esm/implementation/data-type/simple-type.js +26 -0
- package/esm/implementation/execution-context.d.ts +42 -0
- package/esm/implementation/execution-context.js +43 -0
- package/esm/implementation/opra-document.d.ts +26 -0
- package/esm/implementation/opra-document.js +111 -0
- package/esm/implementation/opra-service.d.ts +19 -0
- package/esm/implementation/opra-service.js +55 -0
- package/esm/implementation/resource/container-resource-controller.d.ts +12 -0
- package/esm/implementation/resource/container-resource-controller.js +22 -0
- package/esm/implementation/resource/entity-resource-info.d.ts +24 -0
- package/esm/implementation/resource/entity-resource-info.js +63 -0
- package/esm/implementation/resource/resource-info.d.ts +10 -0
- package/esm/implementation/resource/resource-info.js +20 -0
- package/esm/implementation/schema-generator.d.ts +21 -0
- package/esm/implementation/schema-generator.js +169 -0
- package/esm/index.d.ts +22 -0
- package/esm/index.js +22 -0
- package/esm/interfaces/entity-resource.interface.d.ts +9 -0
- package/esm/interfaces/entity-resource.interface.js +1 -0
- package/esm/interfaces/execution-query.interface.d.ts +102 -0
- package/esm/interfaces/execution-query.interface.js +186 -0
- package/esm/interfaces/http-context.interface.d.ts +23 -0
- package/esm/interfaces/http-context.interface.js +1 -0
- package/esm/interfaces/opra-schema.metadata.d.ts +14 -0
- package/esm/interfaces/opra-schema.metadata.js +1 -0
- package/esm/interfaces/resource-container.interface.d.ts +6 -0
- package/esm/interfaces/resource-container.interface.js +1 -0
- package/esm/services/data-service.d.ts +2 -0
- package/esm/services/data-service.js +5 -0
- package/esm/services/json-data-service.d.ts +9 -0
- package/esm/services/json-data-service.js +10 -0
- package/esm/types.d.ts +11 -0
- package/esm/types.js +1 -0
- package/esm/utils/class-utils.d.ts +6 -0
- package/esm/utils/class-utils.js +30 -0
- package/esm/utils/headers.d.ts +9 -0
- package/esm/utils/headers.js +55 -0
- package/esm/utils/internal-data-types.d.ts +4 -0
- package/esm/utils/internal-data-types.js +41 -0
- package/esm/utils/responsive-object.d.ts +3 -0
- package/esm/utils/responsive-object.js +45 -0
- package/esm/utils/string-path-to-object-tree.d.ts +4 -0
- package/esm/utils/string-path-to-object-tree.js +22 -0
- package/esm/utils/terminal-utils.d.ts +4 -0
- package/esm/utils/terminal-utils.js +4 -0
- package/package.json +77 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraExpressAdapter = void 0;
|
|
4
|
+
const url_1 = require("@opra/url");
|
|
5
|
+
const http_adapter_js_1 = require("./http-adapter.js");
|
|
6
|
+
class OpraExpressAdapter extends http_adapter_js_1.OpraHttpAdapter {
|
|
7
|
+
static init(app, service, options) {
|
|
8
|
+
const adapter = new OpraExpressAdapter(service, options);
|
|
9
|
+
const prefix = '/' + (0, url_1.normalizePath)(options?.prefix, true);
|
|
10
|
+
const userContextResolver = options?.userContext;
|
|
11
|
+
app.use(prefix, (request, response, next) => {
|
|
12
|
+
(async () => {
|
|
13
|
+
const userContext = userContextResolver && await userContextResolver(request);
|
|
14
|
+
const req = new ExpressRequestWrapper(request);
|
|
15
|
+
const res = new ExpressResponseWrapper(response);
|
|
16
|
+
const adapterContext = {
|
|
17
|
+
getRequest: () => req,
|
|
18
|
+
getResponse: () => res
|
|
19
|
+
};
|
|
20
|
+
await adapter.handler(adapterContext, userContext);
|
|
21
|
+
})().catch(e => next(e));
|
|
22
|
+
});
|
|
23
|
+
return adapter;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.OpraExpressAdapter = OpraExpressAdapter;
|
|
27
|
+
class ExpressRequestWrapper {
|
|
28
|
+
instance;
|
|
29
|
+
constructor(instance) {
|
|
30
|
+
this.instance = instance;
|
|
31
|
+
}
|
|
32
|
+
getInstance() {
|
|
33
|
+
return this.instance;
|
|
34
|
+
}
|
|
35
|
+
getMethod() {
|
|
36
|
+
return this.instance.method;
|
|
37
|
+
}
|
|
38
|
+
getUrl() {
|
|
39
|
+
return this.instance.url;
|
|
40
|
+
}
|
|
41
|
+
getHeaderNames() {
|
|
42
|
+
return Object.keys(this.instance.headers);
|
|
43
|
+
}
|
|
44
|
+
getHeader(name) {
|
|
45
|
+
return this.instance.get(name);
|
|
46
|
+
}
|
|
47
|
+
getHeaders() {
|
|
48
|
+
return this.instance.headers;
|
|
49
|
+
}
|
|
50
|
+
getBody() {
|
|
51
|
+
return this.instance.body;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class ExpressResponseWrapper {
|
|
55
|
+
instance;
|
|
56
|
+
constructor(instance) {
|
|
57
|
+
this.instance = instance;
|
|
58
|
+
}
|
|
59
|
+
getInstance() {
|
|
60
|
+
return this.instance;
|
|
61
|
+
}
|
|
62
|
+
getHeaderNames() {
|
|
63
|
+
return this.instance.getHeaderNames();
|
|
64
|
+
}
|
|
65
|
+
getHeader(name) {
|
|
66
|
+
return this.instance.get(name);
|
|
67
|
+
}
|
|
68
|
+
setHeader(name, value) {
|
|
69
|
+
this.instance.setHeader(name, value);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
getStatus() {
|
|
73
|
+
return this.instance.statusCode;
|
|
74
|
+
}
|
|
75
|
+
setStatus(value) {
|
|
76
|
+
// noinspection SuspiciousTypeOfGuard
|
|
77
|
+
this.instance.status(typeof value === 'number'
|
|
78
|
+
? value
|
|
79
|
+
: parseInt(value, 10) || 500);
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
send(body) {
|
|
83
|
+
if (typeof body === 'string' || Buffer.isBuffer(body))
|
|
84
|
+
this.instance.send(body);
|
|
85
|
+
else
|
|
86
|
+
this.instance.json(body);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
end() {
|
|
90
|
+
this.instance.end();
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraHttpAdapter = void 0;
|
|
4
|
+
const url_1 = require("@opra/url");
|
|
5
|
+
const constants_js_1 = require("../../constants.js");
|
|
6
|
+
const index_js_1 = require("../../enums/index.js");
|
|
7
|
+
const index_js_2 = require("../../exception/index.js");
|
|
8
|
+
const execution_query_interface_js_1 = require("../../interfaces/execution-query.interface.js");
|
|
9
|
+
const headers_js_1 = require("../../utils/headers.js");
|
|
10
|
+
const complex_type_js_1 = require("../data-type/complex-type.js");
|
|
11
|
+
const execution_context_js_1 = require("../execution-context.js");
|
|
12
|
+
const container_resource_controller_js_1 = require("../resource/container-resource-controller.js");
|
|
13
|
+
const entity_resource_info_js_1 = require("../resource/entity-resource-info.js");
|
|
14
|
+
const adapter_js_1 = require("./adapter.js");
|
|
15
|
+
class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
16
|
+
prepareExecutionContexts(adapterContext, userContext) {
|
|
17
|
+
const req = adapterContext.getRequest();
|
|
18
|
+
// todo implement batch requests
|
|
19
|
+
if (this.isBatch(adapterContext)) {
|
|
20
|
+
throw new Error('not implemented yet');
|
|
21
|
+
}
|
|
22
|
+
const url = new url_1.OpraURL(req.getUrl());
|
|
23
|
+
return [this.prepareExecutionContext(adapterContext, url, req.getMethod(), headers_js_1.Headers.from(req.getHeaders()), req.getBody(), userContext)];
|
|
24
|
+
}
|
|
25
|
+
prepareExecutionContext(adapterContext, url, method, headers, body, userContext) {
|
|
26
|
+
if (!url.path.size)
|
|
27
|
+
throw new index_js_2.BadRequestError();
|
|
28
|
+
if (method !== 'GET' && url.path.size > 1)
|
|
29
|
+
throw new index_js_2.BadRequestError();
|
|
30
|
+
const query = this.buildQuery(url, method, body);
|
|
31
|
+
if (!query)
|
|
32
|
+
throw new index_js_2.MethodNotAllowedError({
|
|
33
|
+
message: `Method "${method}" is not allowed by target resource`
|
|
34
|
+
});
|
|
35
|
+
const request = new execution_context_js_1.ExecutionRequest({
|
|
36
|
+
query,
|
|
37
|
+
headers,
|
|
38
|
+
params: url.searchParams,
|
|
39
|
+
});
|
|
40
|
+
const response = new execution_context_js_1.ExecutionResponse();
|
|
41
|
+
// noinspection UnnecessaryLocalVariableJS
|
|
42
|
+
const executionContext = new execution_context_js_1.ExecutionContext({
|
|
43
|
+
type: 'http',
|
|
44
|
+
service: this.service,
|
|
45
|
+
request,
|
|
46
|
+
response,
|
|
47
|
+
adapterContext,
|
|
48
|
+
userContext,
|
|
49
|
+
continueOnError: request.query.operationType === 'read'
|
|
50
|
+
});
|
|
51
|
+
return executionContext;
|
|
52
|
+
}
|
|
53
|
+
buildQuery(url, method, body) {
|
|
54
|
+
let container = this.service;
|
|
55
|
+
try {
|
|
56
|
+
let pathIndex = 0;
|
|
57
|
+
const pathLen = url.path.size;
|
|
58
|
+
while (pathIndex < pathLen) {
|
|
59
|
+
let p = url.path.get(pathIndex++);
|
|
60
|
+
const resource = container.getResource(p.resource);
|
|
61
|
+
// Move through path directories (containers)
|
|
62
|
+
if (resource instanceof container_resource_controller_js_1.ContainerResourceController) {
|
|
63
|
+
container = resource;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
method = method.toUpperCase();
|
|
67
|
+
if (resource instanceof entity_resource_info_js_1.EntityResourceInfo) {
|
|
68
|
+
const scope = p.key ? 'instance' : 'collection';
|
|
69
|
+
if (pathIndex < pathLen && !(method === 'GET' && scope === 'instance'))
|
|
70
|
+
return;
|
|
71
|
+
let query;
|
|
72
|
+
switch (method) {
|
|
73
|
+
case 'GET': {
|
|
74
|
+
if (scope === 'collection') {
|
|
75
|
+
query = execution_query_interface_js_1.ExecutionQuery.forSearch(resource, {
|
|
76
|
+
filter: url.searchParams.get('$filter'),
|
|
77
|
+
limit: url.searchParams.get('$limit'),
|
|
78
|
+
skip: url.searchParams.get('$skip'),
|
|
79
|
+
distinct: url.searchParams.get('$distinct'),
|
|
80
|
+
total: url.searchParams.get('$total'),
|
|
81
|
+
sort: url.searchParams.get('$sort'),
|
|
82
|
+
pick: url.searchParams.get('$pick'),
|
|
83
|
+
omit: url.searchParams.get('$omit'),
|
|
84
|
+
include: url.searchParams.get('$include'),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
query = execution_query_interface_js_1.ExecutionQuery.forRead(resource, p.key, {
|
|
89
|
+
pick: url.searchParams.get('$pick'),
|
|
90
|
+
omit: url.searchParams.get('$omit'),
|
|
91
|
+
include: url.searchParams.get('$include')
|
|
92
|
+
});
|
|
93
|
+
// Move through properties
|
|
94
|
+
let nested;
|
|
95
|
+
let path = resource.name;
|
|
96
|
+
while (pathIndex < pathLen) {
|
|
97
|
+
const dataType = nested
|
|
98
|
+
? this.service.getDataType(nested.property.type || 'string')
|
|
99
|
+
: query.resource.dataType;
|
|
100
|
+
if (!(dataType instanceof complex_type_js_1.ComplexType))
|
|
101
|
+
throw new Error(`"${path}" is not a ComplexType and has no properties.`);
|
|
102
|
+
p = url.path.get(pathIndex++);
|
|
103
|
+
path += '.' + p.resource;
|
|
104
|
+
const prop = dataType.properties?.[p.resource];
|
|
105
|
+
if (!prop)
|
|
106
|
+
throw new index_js_2.NotFoundError({ message: `Invalid or unknown resource path (${path})` });
|
|
107
|
+
const q = execution_query_interface_js_1.ExecutionQuery.forProperty(prop);
|
|
108
|
+
if (nested) {
|
|
109
|
+
nested.nested = q;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
query.nested = q;
|
|
113
|
+
}
|
|
114
|
+
nested = q;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'DELETE': {
|
|
120
|
+
if (scope === 'collection') {
|
|
121
|
+
query = execution_query_interface_js_1.ExecutionQuery.forDeleteMany(resource, {
|
|
122
|
+
filter: url.searchParams.get('$filter'),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
query = execution_query_interface_js_1.ExecutionQuery.forDelete(resource, p.key);
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'POST': {
|
|
131
|
+
if (scope === 'collection') {
|
|
132
|
+
query = execution_query_interface_js_1.ExecutionQuery.forCreate(resource, body, {
|
|
133
|
+
pick: url.searchParams.get('$pick'),
|
|
134
|
+
omit: url.searchParams.get('$omit'),
|
|
135
|
+
include: url.searchParams.get('$include')
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case 'PATCH': {
|
|
141
|
+
if (scope === 'collection') {
|
|
142
|
+
query = execution_query_interface_js_1.ExecutionQuery.forUpdateMany(resource, body, {
|
|
143
|
+
filter: url.searchParams.get('$filter')
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
query = execution_query_interface_js_1.ExecutionQuery.forUpdate(resource, p.key, body, {
|
|
148
|
+
pick: url.searchParams.get('$pick'),
|
|
149
|
+
omit: url.searchParams.get('$omit'),
|
|
150
|
+
include: url.searchParams.get('$include')
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return query;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw new index_js_2.InternalServerError();
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
if (e instanceof index_js_2.ApiException)
|
|
164
|
+
throw e;
|
|
165
|
+
throw new index_js_2.BadRequestError({ message: e.message });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async sendResponse(adapterContext, executionContexts) {
|
|
169
|
+
const outputPackets = [];
|
|
170
|
+
for (const ctx of executionContexts) {
|
|
171
|
+
const v = this.createOutput(ctx);
|
|
172
|
+
outputPackets.push(v);
|
|
173
|
+
}
|
|
174
|
+
if (this.isBatch(adapterContext)) {
|
|
175
|
+
// this.writeError([], new InternalServerError({message: 'Not implemented yet'}));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (!outputPackets.length) {
|
|
179
|
+
const err = new index_js_2.NotFoundError();
|
|
180
|
+
outputPackets.push({
|
|
181
|
+
status: err.status,
|
|
182
|
+
body: {
|
|
183
|
+
errors: [err.response]
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const out = outputPackets[0];
|
|
188
|
+
const resp = adapterContext.getResponse();
|
|
189
|
+
resp.setStatus(out.status);
|
|
190
|
+
resp.setHeader(index_js_1.HttpHeaders.Content_Type, 'application/json');
|
|
191
|
+
resp.setHeader(index_js_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
192
|
+
resp.setHeader(index_js_1.HttpHeaders.Pragma, 'no-cache');
|
|
193
|
+
resp.setHeader(index_js_1.HttpHeaders.Expires, '-1');
|
|
194
|
+
resp.setHeader(index_js_1.HttpHeaders.X_Opra_Version, constants_js_1.OpraVersion);
|
|
195
|
+
if (out.headers) {
|
|
196
|
+
for (const [k, v] of Object.entries(out.headers)) {
|
|
197
|
+
resp.setHeader(k, v);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
resp.send(JSON.stringify(out.body));
|
|
201
|
+
}
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
203
|
+
isBatch(adapterContext) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
createOutput(ctx) {
|
|
207
|
+
const { query } = ctx.request;
|
|
208
|
+
// Determine response status
|
|
209
|
+
let status = ctx.response.status;
|
|
210
|
+
if (ctx.response.errors.length) {
|
|
211
|
+
if (!status || status < 400) {
|
|
212
|
+
status = 0;
|
|
213
|
+
for (const e of ctx.response.errors) {
|
|
214
|
+
status = Math.max(status, e.status || status);
|
|
215
|
+
}
|
|
216
|
+
if (status < index_js_1.HttpStatus.BAD_REQUEST)
|
|
217
|
+
status = index_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else
|
|
221
|
+
status = status || (query.operationType === 'create' ? index_js_1.HttpStatus.CREATED : index_js_1.HttpStatus.OK);
|
|
222
|
+
let body;
|
|
223
|
+
let value = ctx.response.value;
|
|
224
|
+
if (query.queryType === 'search') {
|
|
225
|
+
body = {
|
|
226
|
+
// '@origin': ctx.resource.name + (ctx.request.resultPath ? '.' + ctx.request.resultPath : ''),
|
|
227
|
+
items: ctx.response.value,
|
|
228
|
+
total: ctx.response.total
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Move to sub property if result path defined
|
|
233
|
+
if (value && ctx.request.resultPath) {
|
|
234
|
+
const pathArray = ctx.request.resultPath.split('.');
|
|
235
|
+
for (const property of pathArray) {
|
|
236
|
+
value = value && typeof value === 'object' && value[property];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
body = value;
|
|
240
|
+
}
|
|
241
|
+
if (ctx.response.errors?.length) {
|
|
242
|
+
body = body || {};
|
|
243
|
+
body.errors = ctx.response.errors.map(e => e.response);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
status,
|
|
247
|
+
headers: ctx.response.headers,
|
|
248
|
+
body
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async sendError(adapterContext, error) {
|
|
252
|
+
const resp = adapterContext.getResponse();
|
|
253
|
+
resp.setStatus(error.status || 500);
|
|
254
|
+
resp.setHeader(index_js_1.HttpHeaders.Content_Type, 'application/json');
|
|
255
|
+
resp.setHeader(index_js_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
256
|
+
resp.setHeader(index_js_1.HttpHeaders.Pragma, 'no-cache');
|
|
257
|
+
resp.setHeader(index_js_1.HttpHeaders.Expires, '-1');
|
|
258
|
+
resp.setHeader(index_js_1.HttpHeaders.X_Opra_Version, constants_js_1.OpraVersion);
|
|
259
|
+
resp.send(JSON.stringify(error.response));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
exports.OpraHttpAdapter = OpraHttpAdapter;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ComplexType = void 0;
|
|
4
|
+
const responsive_object_js_1 = require("../../utils/responsive-object.js");
|
|
5
|
+
const terminal_utils_js_1 = require("../../utils/terminal-utils.js");
|
|
6
|
+
const data_type_js_1 = require("./data-type.js");
|
|
7
|
+
class ComplexType extends data_type_js_1.DataType {
|
|
8
|
+
ownProperties;
|
|
9
|
+
properties;
|
|
10
|
+
constructor(owner, args, base) {
|
|
11
|
+
super(owner, {
|
|
12
|
+
kind: 'ComplexType',
|
|
13
|
+
...args
|
|
14
|
+
}, base);
|
|
15
|
+
this.ownProperties = args?.properties && (0, responsive_object_js_1.Responsive)(args.properties);
|
|
16
|
+
this.properties = (base?.properties || this.ownProperties) &&
|
|
17
|
+
(0, responsive_object_js_1.Responsive)({ ...base?.properties, ...this.ownProperties });
|
|
18
|
+
}
|
|
19
|
+
get abstract() {
|
|
20
|
+
return !!this._args.abstract;
|
|
21
|
+
}
|
|
22
|
+
get additionalProperties() {
|
|
23
|
+
return this._args.additionalProperties;
|
|
24
|
+
}
|
|
25
|
+
getProperty(name) {
|
|
26
|
+
const t = this.properties?.[name];
|
|
27
|
+
if (!t)
|
|
28
|
+
throw new Error(`"${this.name}" type has no property named "${name}"`);
|
|
29
|
+
return t;
|
|
30
|
+
}
|
|
31
|
+
toString() {
|
|
32
|
+
return `[${Object.getPrototypeOf(this).constructor.name} ${this.name}]`;
|
|
33
|
+
}
|
|
34
|
+
[terminal_utils_js_1.nodeInspectCustom]() {
|
|
35
|
+
return `[${terminal_utils_js_1.colorFgYellow + Object.getPrototypeOf(this).constructor.name + terminal_utils_js_1.colorReset}` +
|
|
36
|
+
` ${terminal_utils_js_1.colorFgMagenta + this.name + terminal_utils_js_1.colorReset}]`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.ComplexType = ComplexType;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataType = void 0;
|
|
4
|
+
class DataType {
|
|
5
|
+
_owner;
|
|
6
|
+
_args;
|
|
7
|
+
base;
|
|
8
|
+
constructor(owner, args, base) {
|
|
9
|
+
this._args = { ...args };
|
|
10
|
+
this._owner = owner;
|
|
11
|
+
if (base) {
|
|
12
|
+
this.base = base;
|
|
13
|
+
Object.setPrototypeOf(this._args, base._args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
get owner() {
|
|
17
|
+
return this._owner;
|
|
18
|
+
}
|
|
19
|
+
get kind() {
|
|
20
|
+
return this._args.kind;
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return this._args.name;
|
|
24
|
+
}
|
|
25
|
+
get description() {
|
|
26
|
+
return this._args.description;
|
|
27
|
+
}
|
|
28
|
+
get ctor() {
|
|
29
|
+
return this._args.ctor;
|
|
30
|
+
}
|
|
31
|
+
is(typeName) {
|
|
32
|
+
return this.name === typeName || !!(this.base && this.base.is(typeName));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.DataType = DataType;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EntityType = void 0;
|
|
4
|
+
const optionals_1 = require("@opra/optionals");
|
|
5
|
+
const complex_type_js_1 = require("./complex-type.js");
|
|
6
|
+
class EntityType extends complex_type_js_1.ComplexType {
|
|
7
|
+
constructor(owner, args, base) {
|
|
8
|
+
super(owner, {
|
|
9
|
+
...args
|
|
10
|
+
}, base);
|
|
11
|
+
this._args.kind = 'EntityType';
|
|
12
|
+
// Try to determine primary key info from SQB
|
|
13
|
+
if (args.ctor) {
|
|
14
|
+
const sqbEntity = optionals_1.SqbConnect.EntityMetadata.get(args.ctor);
|
|
15
|
+
if (sqbEntity?.indexes) {
|
|
16
|
+
const primaryIndex = sqbEntity.indexes.find(x => x.primary);
|
|
17
|
+
if (primaryIndex) {
|
|
18
|
+
if (primaryIndex.columns.length > 1)
|
|
19
|
+
throw new TypeError(`Multi-key indexes is not implemented yet`);
|
|
20
|
+
this._args.primaryKey = primaryIndex.columns[0];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!this.primaryKey)
|
|
25
|
+
throw new TypeError(`You must provide primaryKey fo "${this.name}" entity`);
|
|
26
|
+
if (!this.getProperty(this.primaryKey))
|
|
27
|
+
throw new TypeError(`"${this.name}" entity has no such property named "${this.primaryKey}" which defined as primary key`);
|
|
28
|
+
}
|
|
29
|
+
get primaryKey() {
|
|
30
|
+
return this._args.primaryKey;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.EntityType = EntityType;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SimpleType = void 0;
|
|
4
|
+
const terminal_utils_js_1 = require("../../utils/terminal-utils.js");
|
|
5
|
+
const data_type_js_1 = require("./data-type.js");
|
|
6
|
+
class SimpleType extends data_type_js_1.DataType {
|
|
7
|
+
constructor(owner, args, base) {
|
|
8
|
+
super(owner, {
|
|
9
|
+
kind: 'SimpleType',
|
|
10
|
+
...args
|
|
11
|
+
}, base);
|
|
12
|
+
}
|
|
13
|
+
get type() {
|
|
14
|
+
return this._args.type;
|
|
15
|
+
}
|
|
16
|
+
get format() {
|
|
17
|
+
return this._args.format;
|
|
18
|
+
}
|
|
19
|
+
get default() {
|
|
20
|
+
return this._args.default;
|
|
21
|
+
}
|
|
22
|
+
toString() {
|
|
23
|
+
return `[${Object.getPrototypeOf(this).constructor.name} ${this.name}]`;
|
|
24
|
+
}
|
|
25
|
+
[terminal_utils_js_1.nodeInspectCustom]() {
|
|
26
|
+
return `[${terminal_utils_js_1.colorFgYellow + Object.getPrototypeOf(this).constructor.name + terminal_utils_js_1.colorReset}` +
|
|
27
|
+
` ${terminal_utils_js_1.colorFgMagenta + this.name + terminal_utils_js_1.colorReset}]`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.SimpleType = SimpleType;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExecutionResponse = exports.ExecutionRequest = exports.ExecutionContext = void 0;
|
|
4
|
+
const url_1 = require("@opra/url");
|
|
5
|
+
const headers_js_1 = require("../utils/headers.js");
|
|
6
|
+
class ExecutionContext {
|
|
7
|
+
type;
|
|
8
|
+
service;
|
|
9
|
+
request;
|
|
10
|
+
response;
|
|
11
|
+
adapterContext;
|
|
12
|
+
userContext;
|
|
13
|
+
continueOnError;
|
|
14
|
+
constructor(args) {
|
|
15
|
+
Object.assign(this, args);
|
|
16
|
+
}
|
|
17
|
+
switchToHttp() {
|
|
18
|
+
if (this.type !== 'http')
|
|
19
|
+
throw new Error(`You can't access http context within an ${this.type} context`);
|
|
20
|
+
return this.adapterContext;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.ExecutionContext = ExecutionContext;
|
|
24
|
+
class ExecutionRequest {
|
|
25
|
+
query;
|
|
26
|
+
params;
|
|
27
|
+
headers;
|
|
28
|
+
parentValue;
|
|
29
|
+
resultPath;
|
|
30
|
+
constructor(args) {
|
|
31
|
+
Object.assign(this, args);
|
|
32
|
+
this.params = this.params || new url_1.OpraURLSearchParams();
|
|
33
|
+
this.headers = this.headers || headers_js_1.Headers.create();
|
|
34
|
+
this.resultPath = this.resultPath || '';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.ExecutionRequest = ExecutionRequest;
|
|
38
|
+
class ExecutionResponse {
|
|
39
|
+
headers = headers_js_1.Headers.create();
|
|
40
|
+
errors = [];
|
|
41
|
+
status;
|
|
42
|
+
value;
|
|
43
|
+
total;
|
|
44
|
+
constructor(args) {
|
|
45
|
+
if (args)
|
|
46
|
+
Object.assign(this, args);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.ExecutionResponse = ExecutionResponse;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraDocument = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
|
+
const schema_1 = require("@opra/schema");
|
|
7
|
+
const internal_data_types_js_1 = require("../utils/internal-data-types.js");
|
|
8
|
+
const responsive_object_js_1 = require("../utils/responsive-object.js");
|
|
9
|
+
const terminal_utils_js_1 = require("../utils/terminal-utils.js");
|
|
10
|
+
const complex_type_js_1 = require("./data-type/complex-type.js");
|
|
11
|
+
const entity_type_js_1 = require("./data-type/entity-type.js");
|
|
12
|
+
const simple_type_js_1 = require("./data-type/simple-type.js");
|
|
13
|
+
const schema_generator_js_1 = require("./schema-generator.js");
|
|
14
|
+
class OpraDocument {
|
|
15
|
+
_args;
|
|
16
|
+
_types = (0, responsive_object_js_1.Responsive)();
|
|
17
|
+
constructor(schema) {
|
|
18
|
+
this._args = lodash_1.default.omit(schema, 'types');
|
|
19
|
+
if (schema.types)
|
|
20
|
+
this._addDataTypes(schema.types);
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return this._args.info?.title || '';
|
|
24
|
+
}
|
|
25
|
+
get info() {
|
|
26
|
+
return this._args.info;
|
|
27
|
+
}
|
|
28
|
+
get types() {
|
|
29
|
+
return this._types;
|
|
30
|
+
}
|
|
31
|
+
getDataType(name) {
|
|
32
|
+
const t = this.types[name];
|
|
33
|
+
if (!t)
|
|
34
|
+
throw new Error(`Data type "${name}" does not exists`);
|
|
35
|
+
return t;
|
|
36
|
+
}
|
|
37
|
+
getComplexDataType(name) {
|
|
38
|
+
const t = this.getDataType(name);
|
|
39
|
+
if (!(t instanceof complex_type_js_1.ComplexType))
|
|
40
|
+
throw new Error(`Data type "${name}" is not a ComplexType`);
|
|
41
|
+
return t;
|
|
42
|
+
}
|
|
43
|
+
getEntityDataType(name) {
|
|
44
|
+
const t = this.getDataType(name);
|
|
45
|
+
if (!(t instanceof entity_type_js_1.EntityType))
|
|
46
|
+
throw new Error(`Data type "${name}" is not an EntityType`);
|
|
47
|
+
return t;
|
|
48
|
+
}
|
|
49
|
+
getSimpleDataType(name) {
|
|
50
|
+
const t = this.getDataType(name);
|
|
51
|
+
if (!(t instanceof simple_type_js_1.SimpleType))
|
|
52
|
+
throw new Error(`Data type "${name}" is not a SimpleType`);
|
|
53
|
+
return t;
|
|
54
|
+
}
|
|
55
|
+
toString() {
|
|
56
|
+
return `[${Object.getPrototypeOf(this).constructor.name} ${this.name}]`;
|
|
57
|
+
}
|
|
58
|
+
[terminal_utils_js_1.nodeInspectCustom]() {
|
|
59
|
+
return `[${terminal_utils_js_1.colorFgYellow + Object.getPrototypeOf(this).constructor.name + terminal_utils_js_1.colorReset}` +
|
|
60
|
+
` ${terminal_utils_js_1.colorFgMagenta + this.name + terminal_utils_js_1.colorReset}]`;
|
|
61
|
+
}
|
|
62
|
+
static async create(args) {
|
|
63
|
+
const schema = await schema_generator_js_1.SchemaGenerator.generateDocumentSchema(args);
|
|
64
|
+
return new OpraDocument(schema);
|
|
65
|
+
}
|
|
66
|
+
_addDataTypes(dataTypes) {
|
|
67
|
+
const recursiveSet = new Set();
|
|
68
|
+
const nameSet = new Set(dataTypes.map(x => x.name));
|
|
69
|
+
const processDataType = (schema) => {
|
|
70
|
+
if ((!internal_data_types_js_1.internalDataTypes.has(schema.name) && !nameSet.has(schema.name)) || this.types[schema.name])
|
|
71
|
+
return;
|
|
72
|
+
if (recursiveSet.has(schema.name))
|
|
73
|
+
throw new TypeError(`Recursive dependency detected. ${Array.from(recursiveSet).join('>')}`);
|
|
74
|
+
recursiveSet.add(schema.name);
|
|
75
|
+
let baseType;
|
|
76
|
+
if (schema.base) {
|
|
77
|
+
if (!this.types[schema.base]) {
|
|
78
|
+
const baseSchema = dataTypes.find(dt => dt.name.toLowerCase() === schema.base?.toLowerCase()) ||
|
|
79
|
+
internal_data_types_js_1.internalDataTypes.get(schema.base.toLowerCase());
|
|
80
|
+
if (!baseSchema)
|
|
81
|
+
throw new TypeError(`Base schema (${schema.base}) of data type "${schema.name}" does not exists`);
|
|
82
|
+
baseType = processDataType(baseSchema);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let dataType;
|
|
86
|
+
if (schema_1.OpraSchema.isSimpleType(schema)) {
|
|
87
|
+
if (baseType && !(baseType instanceof simple_type_js_1.SimpleType))
|
|
88
|
+
throw new TypeError(`Can't extend a SimpleType (${schema.name}) from a ComplexType "${baseType.name}"`);
|
|
89
|
+
dataType = new simple_type_js_1.SimpleType(this, schema, baseType);
|
|
90
|
+
}
|
|
91
|
+
else if (schema_1.OpraSchema.isComplexType(schema)) {
|
|
92
|
+
if (baseType && !(baseType instanceof complex_type_js_1.ComplexType))
|
|
93
|
+
throw new TypeError(`Can't extend a ComplexType (${schema.name}) from a SimpleType "${baseType.name}"`);
|
|
94
|
+
dataType = new complex_type_js_1.ComplexType(this, schema, baseType);
|
|
95
|
+
}
|
|
96
|
+
else if (schema_1.OpraSchema.isEntityType(schema)) {
|
|
97
|
+
if (baseType && !(baseType instanceof complex_type_js_1.ComplexType))
|
|
98
|
+
throw new TypeError(`Can't extend an EntityType (${schema.name}) from a SimpleType "${baseType.name}"`);
|
|
99
|
+
dataType = new entity_type_js_1.EntityType(this, schema, baseType);
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
throw new TypeError(`Invalid data type schema`);
|
|
103
|
+
nameSet.delete(schema.name);
|
|
104
|
+
this.types[dataType.name] = dataType;
|
|
105
|
+
recursiveSet.delete(schema.name);
|
|
106
|
+
return dataType;
|
|
107
|
+
};
|
|
108
|
+
dataTypes.forEach(dataType => processDataType(dataType));
|
|
109
|
+
// Sort data types by name
|
|
110
|
+
const newTypes = (0, responsive_object_js_1.Responsive)();
|
|
111
|
+
Object.keys(this.types).sort()
|
|
112
|
+
.forEach(name => newTypes[name] = this.types[name]);
|
|
113
|
+
this._types = newTypes;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.OpraDocument = OpraDocument;
|