@occultist/occultist 0.0.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.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/accept.d.ts +41 -0
- package/dist/accept.js +110 -0
- package/dist/accept.test.d.ts +1 -0
- package/dist/accept.test.js +44 -0
- package/dist/action.test.d.ts +1 -0
- package/dist/action.test.js +1 -0
- package/dist/actions/actionSets.d.ts +23 -0
- package/dist/actions/actionSets.js +49 -0
- package/dist/actions/actions.d.ts +163 -0
- package/dist/actions/actions.js +436 -0
- package/dist/actions/context.d.ts +78 -0
- package/dist/actions/context.js +112 -0
- package/dist/actions/meta.d.ts +49 -0
- package/dist/actions/meta.js +177 -0
- package/dist/actions/path.d.ts +21 -0
- package/dist/actions/path.js +83 -0
- package/dist/actions/path.test.d.ts +1 -0
- package/dist/actions/path.test.js +9 -0
- package/dist/actions/spec.d.ts +214 -0
- package/dist/actions/spec.js +1 -0
- package/dist/actions/types.d.ts +112 -0
- package/dist/actions/types.js +2 -0
- package/dist/actions/writer.d.ts +27 -0
- package/dist/actions/writer.js +140 -0
- package/dist/actions/writer.test.d.ts +1 -0
- package/dist/actions/writer.test.js +42 -0
- package/dist/auth/types.d.ts +14 -0
- package/dist/auth/types.js +1 -0
- package/dist/cache/cache.d.ts +30 -0
- package/dist/cache/cache.js +220 -0
- package/dist/cache/etag.d.ts +17 -0
- package/dist/cache/etag.js +83 -0
- package/dist/cache/etag.test.d.ts +1 -0
- package/dist/cache/etag.test.js +91 -0
- package/dist/cache/memory.d.ts +12 -0
- package/dist/cache/memory.js +36 -0
- package/dist/cache/types.d.ts +175 -0
- package/dist/cache/types.js +4 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +54 -0
- package/dist/jsonld.d.ts +43 -0
- package/dist/jsonld.js +1 -0
- package/dist/makeTypeDefs.d.ts +27 -0
- package/dist/makeTypeDefs.js +70 -0
- package/dist/merge.d.ts +61 -0
- package/dist/merge.js +1 -0
- package/dist/mod.d.ts +14 -0
- package/dist/mod.js +14 -0
- package/dist/processAction.d.ts +15 -0
- package/dist/processAction.js +512 -0
- package/dist/registry.d.ts +88 -0
- package/dist/registry.js +314 -0
- package/dist/registry.test.d.ts +1 -0
- package/dist/registry.test.js +133 -0
- package/dist/request.d.ts +29 -0
- package/dist/request.js +118 -0
- package/dist/scopes.d.ts +35 -0
- package/dist/scopes.js +121 -0
- package/dist/scopes.test.d.ts +1 -0
- package/dist/scopes.test.js +55 -0
- package/dist/transformers/fileTransformer.d.ts +1 -0
- package/dist/transformers/fileTransformer.js +8 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js +1 -0
- package/dist/utils/alwaysArray.d.ts +1 -0
- package/dist/utils/alwaysArray.js +9 -0
- package/dist/utils/contextBuilder.d.ts +9 -0
- package/dist/utils/contextBuilder.js +82 -0
- package/dist/utils/getActionContext.d.ts +7 -0
- package/dist/utils/getActionContext.js +48 -0
- package/dist/utils/getInternalName.d.ts +6 -0
- package/dist/utils/getInternalName.js +7 -0
- package/dist/utils/getParamLocation.d.ts +2 -0
- package/dist/utils/getParamLocation.js +6 -0
- package/dist/utils/getPropertyValueSpecifications.d.ts +2 -0
- package/dist/utils/getPropertyValueSpecifications.js +49 -0
- package/dist/utils/getRequestBodyValues.d.ts +11 -0
- package/dist/utils/getRequestBodyValues.js +122 -0
- package/dist/utils/getRequestIRIValues.d.ts +14 -0
- package/dist/utils/getRequestIRIValues.js +133 -0
- package/dist/utils/isBodyInit.d.ts +1 -0
- package/dist/utils/isBodyInit.js +21 -0
- package/dist/utils/isNil.d.ts +1 -0
- package/dist/utils/isNil.js +4 -0
- package/dist/utils/isObject.d.ts +6 -0
- package/dist/utils/isObject.js +6 -0
- package/dist/utils/isPopulatedObject.d.ts +5 -0
- package/dist/utils/isPopulatedObject.js +8 -0
- package/dist/utils/isPopulatedString.d.ts +1 -0
- package/dist/utils/isPopulatedString.js +4 -0
- package/dist/utils/joinPaths.d.ts +1 -0
- package/dist/utils/joinPaths.js +31 -0
- package/dist/utils/makeAppendProblemDetails.d.ts +14 -0
- package/dist/utils/makeAppendProblemDetails.js +26 -0
- package/dist/utils/makeURLPattern.d.ts +5 -0
- package/dist/utils/makeURLPattern.js +12 -0
- package/dist/utils/normalizeURL.d.ts +4 -0
- package/dist/utils/normalizeURL.js +11 -0
- package/dist/utils/parseSearchParams.d.ts +3 -0
- package/dist/utils/parseSearchParams.js +24 -0
- package/dist/utils/preferredMediaTypes.d.ts +42 -0
- package/dist/utils/preferredMediaTypes.js +149 -0
- package/dist/utils/urlToIRI.d.ts +1 -0
- package/dist/utils/urlToIRI.js +8 -0
- package/dist/utils/validateSpecValue.d.ts +1 -0
- package/dist/utils/validateSpecValue.js +1 -0
- package/dist/validators.d.ts +16 -0
- package/dist/validators.js +134 -0
- package/lib/accept.test.ts +55 -0
- package/lib/accept.ts +147 -0
- package/lib/action.test.ts +2 -0
- package/lib/actions/actionSets.ts +88 -0
- package/lib/actions/actions.ts +795 -0
- package/lib/actions/context.ts +170 -0
- package/lib/actions/meta.ts +251 -0
- package/lib/actions/path.test.ts +15 -0
- package/lib/actions/path.ts +99 -0
- package/lib/actions/spec.ts +545 -0
- package/lib/actions/types.ts +146 -0
- package/lib/actions/writer.test.ts +57 -0
- package/lib/actions/writer.ts +176 -0
- package/lib/auth/types.ts +22 -0
- package/lib/cache/cache.ts +291 -0
- package/lib/cache/etag.test.ts +122 -0
- package/lib/cache/etag.ts +106 -0
- package/lib/cache/memory.ts +52 -0
- package/lib/cache/types.ts +240 -0
- package/lib/errors.ts +66 -0
- package/lib/jsonld.ts +67 -0
- package/lib/makeTypeDefs.ts +138 -0
- package/lib/merge.ts +86 -0
- package/lib/mod.ts +14 -0
- package/lib/processAction.ts +690 -0
- package/lib/registry.test.ts +174 -0
- package/lib/registry.ts +455 -0
- package/lib/request.ts +153 -0
- package/lib/scopes.test.ts +70 -0
- package/lib/scopes.ts +178 -0
- package/lib/transformers/fileTransformer.ts +10 -0
- package/lib/types.ts +13 -0
- package/lib/utils/alwaysArray.ts +10 -0
- package/lib/utils/contextBuilder.ts +111 -0
- package/lib/utils/getActionContext.ts +76 -0
- package/lib/utils/getInternalName.ts +15 -0
- package/lib/utils/getParamLocation.ts +14 -0
- package/lib/utils/getPropertyValueSpecifications.ts +76 -0
- package/lib/utils/getRequestBodyValues.ts +155 -0
- package/lib/utils/getRequestIRIValues.ts +201 -0
- package/lib/utils/isBodyInit.ts +22 -0
- package/lib/utils/isNil.ts +4 -0
- package/lib/utils/isObject.ts +8 -0
- package/lib/utils/isPopulatedObject.ts +9 -0
- package/lib/utils/isPopulatedString.ts +4 -0
- package/lib/utils/joinPaths.ts +36 -0
- package/lib/utils/makeAppendProblemDetails.ts +57 -0
- package/lib/utils/makeURLPattern.ts +18 -0
- package/lib/utils/normalizeURL.ts +15 -0
- package/lib/utils/parseSearchParams.ts +36 -0
- package/lib/utils/preferredMediaTypes.ts +220 -0
- package/lib/utils/urlToIRI.ts +11 -0
- package/lib/utils/validateSpecValue.ts +0 -0
- package/lib/validators.ts +186 -0
- package/package.json +41 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { JsonPointer } from 'json-ptr';
|
|
2
|
+
import { getInternalName } from "./utils/getInternalName.js";
|
|
3
|
+
import { getRequestBodyValues } from "./utils/getRequestBodyValues.js";
|
|
4
|
+
import { getRequestIRIValues } from "./utils/getRequestIRIValues.js";
|
|
5
|
+
import { isNil } from "./utils/isNil.js";
|
|
6
|
+
import { isObject } from "./utils/isObject.js";
|
|
7
|
+
import { isPopulatedObject } from "./utils/isPopulatedObject.js";
|
|
8
|
+
import { makeAppendProblemDetails } from "./utils/makeAppendProblemDetails.js";
|
|
9
|
+
import { failsRequiredRequirement, failsTypeRequirement, failsContentTypeRequirement, failsMaxValue, failsMinValue, failValueMinLength, failValueMaxLength, failsStepValue, failsPatternValue, failsValidator, isObjectArraySpec, isObjectSpec, isArraySpec } from "./validators.js";
|
|
10
|
+
import { InvalidActionParamsError, ProblemDetailsError } from "./errors.js";
|
|
11
|
+
import { alwaysArray } from "./utils/alwaysArray.js";
|
|
12
|
+
export async function processAction({ iri, req, spec, state, action, }) {
|
|
13
|
+
let httpStatus = null;
|
|
14
|
+
const payload = {};
|
|
15
|
+
const transformers = {};
|
|
16
|
+
const refs = {};
|
|
17
|
+
const appendProblemDetailsParam = makeAppendProblemDetails(refs);
|
|
18
|
+
console.log('SPEC', spec);
|
|
19
|
+
let params;
|
|
20
|
+
let query;
|
|
21
|
+
let iriValues;
|
|
22
|
+
let bodyValues;
|
|
23
|
+
{
|
|
24
|
+
const result = getRequestIRIValues({ iri, action });
|
|
25
|
+
params = result.pathValues;
|
|
26
|
+
query = result.queryValues;
|
|
27
|
+
iriValues = result.iriValues;
|
|
28
|
+
}
|
|
29
|
+
{
|
|
30
|
+
const result = await getRequestBodyValues({ req, action });
|
|
31
|
+
bodyValues = result.bodyValues;
|
|
32
|
+
}
|
|
33
|
+
const mergedValues = Object.assign(Object.create(null), bodyValues, iriValues);
|
|
34
|
+
function hasRequiredFields(specObject) {
|
|
35
|
+
if (!isPopulatedObject(specObject)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
for (const specValue of Object.values(specObject)) {
|
|
39
|
+
if (specValue.valueRequired && specValue.valueName == null) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function handleValueSpecValue({ paramName, pointer, value, specValue, }) {
|
|
46
|
+
if (typeof value === 'undefined' && !specValue.valueRequired) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
else if (value === null && !specValue.valueRequired) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (failsRequiredRequirement(value, specValue)) {
|
|
53
|
+
appendProblemDetailsParam({
|
|
54
|
+
status: 400,
|
|
55
|
+
param: {
|
|
56
|
+
name: paramName,
|
|
57
|
+
reason: `Value required`,
|
|
58
|
+
pointer,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
else if (failsTypeRequirement(value, specValue)) {
|
|
64
|
+
if (specValue.dataType === 'boolean' ||
|
|
65
|
+
specValue.dataType === 'number' ||
|
|
66
|
+
specValue.dataType === 'file') {
|
|
67
|
+
appendProblemDetailsParam({
|
|
68
|
+
status: 400,
|
|
69
|
+
param: {
|
|
70
|
+
name: paramName,
|
|
71
|
+
reason: `Invalid datatype provided when a ${specValue.dataType} is expected`,
|
|
72
|
+
pointer,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
appendProblemDetailsParam({
|
|
78
|
+
status: 400,
|
|
79
|
+
param: {
|
|
80
|
+
name: paramName,
|
|
81
|
+
reason: `Invalid datatype provided`,
|
|
82
|
+
pointer,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
else if (failsContentTypeRequirement(value, specValue)) {
|
|
89
|
+
appendProblemDetailsParam({
|
|
90
|
+
status: 400,
|
|
91
|
+
param: {
|
|
92
|
+
name: paramName,
|
|
93
|
+
reason: `File does not meet content type requirements for upload`,
|
|
94
|
+
pointer,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else if (failsMaxValue(value, specValue)) {
|
|
99
|
+
appendProblemDetailsParam({
|
|
100
|
+
status: 400,
|
|
101
|
+
param: {
|
|
102
|
+
name: paramName,
|
|
103
|
+
reason: `Value too large`,
|
|
104
|
+
pointer,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
else if (failsMinValue(value, specValue)) {
|
|
110
|
+
appendProblemDetailsParam({
|
|
111
|
+
status: 400,
|
|
112
|
+
param: {
|
|
113
|
+
name: paramName,
|
|
114
|
+
reason: `Value too small`,
|
|
115
|
+
pointer,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
else if (failValueMinLength(value, specValue)) {
|
|
121
|
+
appendProblemDetailsParam({
|
|
122
|
+
status: 400,
|
|
123
|
+
param: {
|
|
124
|
+
name: paramName,
|
|
125
|
+
reason: `Value's length too small`,
|
|
126
|
+
pointer,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
else if (failValueMaxLength(value, specValue)) {
|
|
132
|
+
appendProblemDetailsParam({
|
|
133
|
+
status: 400,
|
|
134
|
+
param: {
|
|
135
|
+
name: paramName,
|
|
136
|
+
reason: `Value's length too large`,
|
|
137
|
+
pointer,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
else if (failsStepValue(value, specValue)) {
|
|
143
|
+
appendProblemDetailsParam({
|
|
144
|
+
status: 400,
|
|
145
|
+
param: {
|
|
146
|
+
name: paramName,
|
|
147
|
+
reason: `Value does not meet step requirements`,
|
|
148
|
+
pointer,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
else if (failsPatternValue(value, specValue)) {
|
|
154
|
+
appendProblemDetailsParam({
|
|
155
|
+
status: 400,
|
|
156
|
+
param: {
|
|
157
|
+
name: paramName,
|
|
158
|
+
reason: `Value does not meet pattern requirements`,
|
|
159
|
+
pointer,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
else if (failsValidator(value, specValue)) {
|
|
165
|
+
appendProblemDetailsParam({
|
|
166
|
+
status: 400,
|
|
167
|
+
param: {
|
|
168
|
+
name: paramName,
|
|
169
|
+
reason: `Value is not valid`,
|
|
170
|
+
pointer,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const transformer = specValue.transformer;
|
|
176
|
+
if (typeof transformer !== 'undefined') {
|
|
177
|
+
// deno-lint-ignore no-async-promise-executor
|
|
178
|
+
transformers[pointer] = new Promise(async (resolve, reject) => {
|
|
179
|
+
try {
|
|
180
|
+
resolve(await transformer(value, state));
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
if (err instanceof InvalidActionParamsError) {
|
|
184
|
+
if (typeof httpStatus !== 'number') {
|
|
185
|
+
httpStatus = err.status;
|
|
186
|
+
}
|
|
187
|
+
else if (httpStatus !== err.status) {
|
|
188
|
+
httpStatus = 400;
|
|
189
|
+
}
|
|
190
|
+
appendProblemDetailsParam({
|
|
191
|
+
param: {
|
|
192
|
+
name: paramName,
|
|
193
|
+
reason: err.message,
|
|
194
|
+
pointer,
|
|
195
|
+
},
|
|
196
|
+
status: 400,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
appendProblemDetailsParam({
|
|
201
|
+
param: {
|
|
202
|
+
name: paramName,
|
|
203
|
+
reason: `Failed to cast value`,
|
|
204
|
+
pointer,
|
|
205
|
+
},
|
|
206
|
+
status: 400,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
reject(err);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
return value;
|
|
215
|
+
}
|
|
216
|
+
function handleArraySpecValue({ paramName, pointer, value: parentValue, specValue, }) {
|
|
217
|
+
if (typeof parentValue === 'undefined' && !specValue.valueRequired) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
else if (parentValue === null && !specValue.valueRequired) {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
if (failsTypeRequirement(parentValue, specValue)) {
|
|
224
|
+
throw new Error('Nope');
|
|
225
|
+
appendProblemDetailsParam({
|
|
226
|
+
param: {
|
|
227
|
+
name: paramName,
|
|
228
|
+
reason: `Value required`,
|
|
229
|
+
pointer,
|
|
230
|
+
},
|
|
231
|
+
status: 400,
|
|
232
|
+
});
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const arrayValue = alwaysArray(parentValue);
|
|
236
|
+
if (failValueMinLength(arrayValue, specValue)) {
|
|
237
|
+
appendProblemDetailsParam({
|
|
238
|
+
param: {
|
|
239
|
+
name: paramName,
|
|
240
|
+
reason: `Value's length too small`,
|
|
241
|
+
pointer,
|
|
242
|
+
},
|
|
243
|
+
status: 400,
|
|
244
|
+
});
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
else if (failValueMaxLength(arrayValue, specValue)) {
|
|
248
|
+
appendProblemDetailsParam({
|
|
249
|
+
param: {
|
|
250
|
+
name: paramName,
|
|
251
|
+
reason: `Value's length too large`,
|
|
252
|
+
pointer,
|
|
253
|
+
},
|
|
254
|
+
status: 400,
|
|
255
|
+
});
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const sanitizedValue = [];
|
|
259
|
+
const childSpecValue = {
|
|
260
|
+
...specValue,
|
|
261
|
+
multipleValues: undefined,
|
|
262
|
+
};
|
|
263
|
+
for (let index = 0; index < arrayValue.length; index++) {
|
|
264
|
+
const value = arrayValue[index];
|
|
265
|
+
sanitizedValue.push(handleValueSpecValue({
|
|
266
|
+
paramName,
|
|
267
|
+
value,
|
|
268
|
+
specValue: childSpecValue,
|
|
269
|
+
pointer: `${pointer}/${index}`,
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
return sanitizedValue;
|
|
273
|
+
}
|
|
274
|
+
function handleObjectSpecValue({ paramName, pointer, value: parentValue, specValue: parentSpecValue, }) {
|
|
275
|
+
if (failsTypeRequirement(parentValue, parentSpecValue)) {
|
|
276
|
+
appendProblemDetailsParam({
|
|
277
|
+
param: {
|
|
278
|
+
name: paramName,
|
|
279
|
+
reason: `Value required`,
|
|
280
|
+
pointer,
|
|
281
|
+
},
|
|
282
|
+
status: 400,
|
|
283
|
+
});
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
else if (!isObject(parentValue)) {
|
|
287
|
+
appendProblemDetailsParam({
|
|
288
|
+
param: {
|
|
289
|
+
name: paramName,
|
|
290
|
+
reason: `Object expected`,
|
|
291
|
+
pointer,
|
|
292
|
+
},
|
|
293
|
+
status: 400,
|
|
294
|
+
});
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
for (const [paramName, specValue] of Object.entries(parentSpecValue.properties)) {
|
|
298
|
+
if (specValue.valueRequired && isNil(parentValue[paramName])) {
|
|
299
|
+
appendProblemDetailsParam({
|
|
300
|
+
param: {
|
|
301
|
+
name: paramName,
|
|
302
|
+
reason: `Value required`,
|
|
303
|
+
pointer: `${pointer}/${paramName}`,
|
|
304
|
+
},
|
|
305
|
+
status: 400,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const sanitizedValue = {};
|
|
310
|
+
const specValue = parentSpecValue.properties[paramName];
|
|
311
|
+
if (typeof specValue !== 'undefined') {
|
|
312
|
+
for (const [paramNameX, value] of Object.entries(parentValue)) {
|
|
313
|
+
const internalName = getInternalName({
|
|
314
|
+
paramName,
|
|
315
|
+
specValue,
|
|
316
|
+
});
|
|
317
|
+
sanitizedValue[internalName] = handleSpecValue({
|
|
318
|
+
paramName: paramNameX,
|
|
319
|
+
value,
|
|
320
|
+
specValue,
|
|
321
|
+
pointer: `${pointer}/${internalName}`,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return sanitizedValue;
|
|
326
|
+
}
|
|
327
|
+
function handleObjectArraySpecValue({ paramName, pointer: parentPointer, value: parentValue, specValue, }) {
|
|
328
|
+
if (failsTypeRequirement(parentValue, specValue)) {
|
|
329
|
+
appendProblemDetailsParam({
|
|
330
|
+
param: {
|
|
331
|
+
name: paramName,
|
|
332
|
+
reason: `Value required`,
|
|
333
|
+
pointer: parentPointer,
|
|
334
|
+
},
|
|
335
|
+
status: 400,
|
|
336
|
+
});
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
const arrayValue = alwaysArray(parentValue);
|
|
340
|
+
if (failValueMinLength(arrayValue, specValue)) {
|
|
341
|
+
appendProblemDetailsParam({
|
|
342
|
+
param: {
|
|
343
|
+
name: paramName,
|
|
344
|
+
reason: `Value's length too small`,
|
|
345
|
+
pointer: parentPointer,
|
|
346
|
+
},
|
|
347
|
+
status: 400,
|
|
348
|
+
});
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
else if (failValueMaxLength(arrayValue, specValue)) {
|
|
352
|
+
appendProblemDetailsParam({
|
|
353
|
+
param: {
|
|
354
|
+
name: paramName,
|
|
355
|
+
reason: `Value's length too large`,
|
|
356
|
+
pointer: parentPointer,
|
|
357
|
+
},
|
|
358
|
+
status: 400,
|
|
359
|
+
});
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const objectSpecValue = {
|
|
363
|
+
...specValue,
|
|
364
|
+
multipleValues: undefined,
|
|
365
|
+
};
|
|
366
|
+
const sanitizedArrayValue = [];
|
|
367
|
+
for (let index = 0; index < arrayValue.length; index++) {
|
|
368
|
+
const value = arrayValue[index];
|
|
369
|
+
// deno-lint-ignore no-explicit-any
|
|
370
|
+
const sanitizedValue = {};
|
|
371
|
+
const pointer = `${parentPointer}/${index}`;
|
|
372
|
+
for (const [paramName, objectSpec] of Object.entries(objectSpecValue.properties)) {
|
|
373
|
+
const internalName = getInternalName({
|
|
374
|
+
paramName,
|
|
375
|
+
specValue: objectSpec,
|
|
376
|
+
});
|
|
377
|
+
if (!isPopulatedObject(value)) {
|
|
378
|
+
appendProblemDetailsParam({
|
|
379
|
+
status: 400,
|
|
380
|
+
param: {
|
|
381
|
+
name: paramName,
|
|
382
|
+
reason: `Object expected`,
|
|
383
|
+
pointer,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
sanitizedValue[internalName] = handleSpecValue({
|
|
390
|
+
paramName,
|
|
391
|
+
value: value[paramName],
|
|
392
|
+
specValue: objectSpec,
|
|
393
|
+
pointer: `${pointer}/${internalName}`,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
sanitizedArrayValue.push(sanitizedValue);
|
|
398
|
+
}
|
|
399
|
+
return sanitizedArrayValue;
|
|
400
|
+
}
|
|
401
|
+
function handleSpecValue({ paramName, pointer, value, specValue, }) {
|
|
402
|
+
if (isObjectArraySpec(specValue)) {
|
|
403
|
+
return handleObjectArraySpecValue({
|
|
404
|
+
paramName,
|
|
405
|
+
pointer,
|
|
406
|
+
value,
|
|
407
|
+
specValue,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
else if (isObjectSpec(specValue)) {
|
|
411
|
+
return handleObjectSpecValue({
|
|
412
|
+
paramName,
|
|
413
|
+
pointer,
|
|
414
|
+
value,
|
|
415
|
+
specValue,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
else if (isArraySpec(specValue)) {
|
|
419
|
+
return handleArraySpecValue({
|
|
420
|
+
paramName,
|
|
421
|
+
pointer,
|
|
422
|
+
value,
|
|
423
|
+
specValue,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
return handleValueSpecValue({
|
|
427
|
+
paramName,
|
|
428
|
+
pointer,
|
|
429
|
+
value,
|
|
430
|
+
specValue,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (hasRequiredFields(spec) && !isPopulatedObject(mergedValues)) {
|
|
434
|
+
throw new ProblemDetailsError(httpStatus || 400, {
|
|
435
|
+
title: `This action requires a request body`,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
else if (!isPopulatedObject(mergedValues)) {
|
|
439
|
+
return {
|
|
440
|
+
params,
|
|
441
|
+
query,
|
|
442
|
+
payload: payload,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
for (const [paramName, specItem] of Object.entries(spec)) {
|
|
446
|
+
if (specItem.valueRequired && isObject(mergedValues) && isNil(mergedValues[paramName])) {
|
|
447
|
+
appendProblemDetailsParam({
|
|
448
|
+
param: {
|
|
449
|
+
name: paramName,
|
|
450
|
+
reason: `Value required`,
|
|
451
|
+
pointer: `#/${paramName}`,
|
|
452
|
+
},
|
|
453
|
+
status: 400,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
for (const [paramName, value] of Object.entries(mergedValues)) {
|
|
458
|
+
if (!Object.hasOwn(spec, paramName)) {
|
|
459
|
+
console.log(spec);
|
|
460
|
+
throw new ProblemDetailsError(httpStatus || 400, {
|
|
461
|
+
title: `Invalid request`,
|
|
462
|
+
errors: [
|
|
463
|
+
{
|
|
464
|
+
name: paramName,
|
|
465
|
+
reason: 'Unexpected value',
|
|
466
|
+
pointer: `#/${paramName}`,
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
const specValue = spec[paramName];
|
|
472
|
+
if (specValue.readonlyValue) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const internalName = getInternalName({
|
|
476
|
+
paramName,
|
|
477
|
+
specValue,
|
|
478
|
+
});
|
|
479
|
+
payload[internalName] = handleSpecValue({
|
|
480
|
+
paramName,
|
|
481
|
+
pointer: `#/${internalName}`,
|
|
482
|
+
value,
|
|
483
|
+
specValue,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
const promises = [];
|
|
487
|
+
if (refs.problemDetails) {
|
|
488
|
+
throw new ProblemDetailsError(refs.httpStatus || 400, refs.problemDetails);
|
|
489
|
+
}
|
|
490
|
+
for (const [pointer, transformer] of Object.entries(transformers)) {
|
|
491
|
+
promises.push(
|
|
492
|
+
// deno-lint-ignore no-async-promise-executor
|
|
493
|
+
new Promise(async (resolve) => {
|
|
494
|
+
try {
|
|
495
|
+
const value = await transformer;
|
|
496
|
+
JsonPointer.set(payload, pointer, value, true);
|
|
497
|
+
// deno-lint-ignore no-empty
|
|
498
|
+
}
|
|
499
|
+
catch { }
|
|
500
|
+
resolve();
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
await Promise.all(promises);
|
|
504
|
+
if (refs.problemDetails) {
|
|
505
|
+
throw new ProblemDetailsError(refs.httpStatus || 400, refs.problemDetails);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
params,
|
|
509
|
+
query,
|
|
510
|
+
payload: payload,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Accept } from "./accept.js";
|
|
2
|
+
import { ActionAuth, HandlerDefinition } from "./actions/actions.js";
|
|
3
|
+
import { type ActionMatchResult, ActionSet } from "./actions/actionSets.js";
|
|
4
|
+
import type { ImplementedAction } from "./actions/types.js";
|
|
5
|
+
import { Scope } from './scopes.js';
|
|
6
|
+
import { IncomingMessage, type ServerResponse } from "node:http";
|
|
7
|
+
import type { Merge } from "./actions/spec.js";
|
|
8
|
+
import type { ContextState, Middleware } from "./actions/spec.js";
|
|
9
|
+
export interface Callable<State extends ContextState = ContextState> {
|
|
10
|
+
method(method: string, name: string, path: string): ActionAuth<State>;
|
|
11
|
+
}
|
|
12
|
+
export declare class HTTP<State extends ContextState = ContextState> {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(callable: Callable<State>);
|
|
15
|
+
trace(name: string, path: string): ActionAuth<State>;
|
|
16
|
+
options(name: string, path: string): ActionAuth<State>;
|
|
17
|
+
head(name: string, path: string): ActionAuth<State>;
|
|
18
|
+
get(name: string, path: string): ActionAuth<State>;
|
|
19
|
+
put(name: string, path: string): ActionAuth<State>;
|
|
20
|
+
patch(name: string, path: string): ActionAuth<State>;
|
|
21
|
+
post(name: string, path: string): ActionAuth<State>;
|
|
22
|
+
delete(name: string, path: string): ActionAuth<State>;
|
|
23
|
+
}
|
|
24
|
+
export type IndexMatchArgs = {
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare class IndexEntry {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(actionSets: ActionSet[]);
|
|
30
|
+
match(method: string, path: string, accept: Accept): null | ActionMatchResult;
|
|
31
|
+
}
|
|
32
|
+
export type RegistryEvents = 'beforefinalize' | 'afterfinalize';
|
|
33
|
+
export type RegistryArgs = {
|
|
34
|
+
rootIRI: string;
|
|
35
|
+
serverTiming?: boolean;
|
|
36
|
+
};
|
|
37
|
+
export declare class Registry<State extends ContextState = ContextState> implements Callable<State> {
|
|
38
|
+
#private;
|
|
39
|
+
constructor(args: RegistryArgs);
|
|
40
|
+
scope(path: string): Scope<State>;
|
|
41
|
+
get rootIRI(): string;
|
|
42
|
+
get path(): string;
|
|
43
|
+
get http(): HTTP<State>;
|
|
44
|
+
get actions(): Array<ImplementedAction>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns the first action using the given action name. A content type
|
|
47
|
+
* can be provided to select another action going by the same name
|
|
48
|
+
* and returning a different content type.
|
|
49
|
+
*
|
|
50
|
+
* @param name - The name of the action.
|
|
51
|
+
* @param contentType - The action's content type.
|
|
52
|
+
*/
|
|
53
|
+
get(name: string, contentType?: string): ImplementedAction | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Returns a list of all action handler definitions.
|
|
56
|
+
*/
|
|
57
|
+
get handlers(): HandlerDefinition[];
|
|
58
|
+
/**
|
|
59
|
+
* Queries all handler definitions.
|
|
60
|
+
*
|
|
61
|
+
* @param args.method The HTTP method the action should handle.
|
|
62
|
+
* @param args.contentType A content type, or list of content types the action
|
|
63
|
+
* should handle. If a list is given the action
|
|
64
|
+
* will be included if it matches one content type
|
|
65
|
+
* in the list.
|
|
66
|
+
* @param args.meta A meta value, such as a unique symbol, which the action
|
|
67
|
+
* should have in its meta object.
|
|
68
|
+
*/
|
|
69
|
+
query({ method, contentType, meta, }?: {
|
|
70
|
+
method?: string | string[];
|
|
71
|
+
contentType?: string | string[];
|
|
72
|
+
meta?: string | symbol;
|
|
73
|
+
}): HandlerDefinition[];
|
|
74
|
+
/**
|
|
75
|
+
* Creates an action for any HTTP method.
|
|
76
|
+
*
|
|
77
|
+
* @param method The HTTP method name.
|
|
78
|
+
* @param name Name for the action being produced.
|
|
79
|
+
* @param path Path the action responds to.
|
|
80
|
+
*/
|
|
81
|
+
method(method: string, name: string, path: string): ActionAuth<State>;
|
|
82
|
+
use<const MiddlewareState extends ContextState = ContextState>(middleware: Middleware<MiddlewareState>): Registry<Merge<State, MiddlewareState>>;
|
|
83
|
+
finalize(): void;
|
|
84
|
+
handleRequest(req: Request): Promise<Response>;
|
|
85
|
+
handleRequest(req: IncomingMessage, res: ServerResponse): Promise<ServerResponse>;
|
|
86
|
+
addEventListener(type: RegistryEvents, callback: EventListener): void;
|
|
87
|
+
removeEventListener(type: RegistryEvents, callback: EventListener): void;
|
|
88
|
+
}
|