@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,690 @@
|
|
|
1
|
+
import { JsonPointer } from 'json-ptr';
|
|
2
|
+
import type { JSONValue } from "./jsonld.js";
|
|
3
|
+
import { getInternalName } from "./utils/getInternalName.js";
|
|
4
|
+
import { type BodyValue, getRequestBodyValues } from "./utils/getRequestBodyValues.js";
|
|
5
|
+
import { type IRIValue, getRequestIRIValues } from "./utils/getRequestIRIValues.js";
|
|
6
|
+
import { isNil } from "./utils/isNil.js";
|
|
7
|
+
import { isObject } from "./utils/isObject.js";
|
|
8
|
+
import { isPopulatedObject } from "./utils/isPopulatedObject.js";
|
|
9
|
+
import { type ProblemDetailsParamsRefs, makeAppendProblemDetails } from "./utils/makeAppendProblemDetails.js";
|
|
10
|
+
import { failsRequiredRequirement, failsTypeRequirement, failsContentTypeRequirement, failsMaxValue, failsMinValue, failValueMinLength, failValueMaxLength, failsStepValue, failsPatternValue, failsValidator, isObjectArraySpec, isObjectSpec, isArraySpec } from "./validators.js";
|
|
11
|
+
import { InvalidActionParamsError, ProblemDetailsError } from "./errors.js";
|
|
12
|
+
import { alwaysArray } from "./utils/alwaysArray.js";
|
|
13
|
+
import type { ImplementedAction } from "./actions/types.js";
|
|
14
|
+
import type { ActionPayload, ActionSpec, ArraySpec, ContextState, ObjectArraySpec, ObjectSpec, ParsedIRIValues, PropertySpec, SpecValue, ValueSpec } from "./actions/spec.js";
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export type ProcessActionArgs<
|
|
20
|
+
State extends ContextState = ContextState,
|
|
21
|
+
Spec extends ActionSpec<ContextState> = ActionSpec<ContextState>,
|
|
22
|
+
> = {
|
|
23
|
+
iri: string;
|
|
24
|
+
req: Request;
|
|
25
|
+
spec: Spec,
|
|
26
|
+
state: State;
|
|
27
|
+
action: ImplementedAction<State, Spec>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ProcessActionResult<
|
|
31
|
+
Spec extends ActionSpec<ContextState> = ActionSpec<ContextState>,
|
|
32
|
+
> = {
|
|
33
|
+
params: ParsedIRIValues;
|
|
34
|
+
query: ParsedIRIValues;
|
|
35
|
+
payload: ActionPayload<Spec>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export async function processAction<
|
|
39
|
+
State extends ContextState = ContextState,
|
|
40
|
+
Spec extends ActionSpec<ContextState> = ActionSpec<ContextState>,
|
|
41
|
+
>({
|
|
42
|
+
iri,
|
|
43
|
+
req,
|
|
44
|
+
spec,
|
|
45
|
+
state,
|
|
46
|
+
action,
|
|
47
|
+
}: ProcessActionArgs<State, Spec>): Promise<ProcessActionResult<Spec>> {
|
|
48
|
+
let httpStatus: number | null = null;
|
|
49
|
+
const payload: Partial<ActionPayload<Spec>> = {};
|
|
50
|
+
const transformers: Record<string, Promise<unknown>> = {};
|
|
51
|
+
const refs: ProblemDetailsParamsRefs = {};
|
|
52
|
+
const appendProblemDetailsParam = makeAppendProblemDetails(refs);
|
|
53
|
+
|
|
54
|
+
console.log('SPEC', spec);
|
|
55
|
+
|
|
56
|
+
let params: ParsedIRIValues;
|
|
57
|
+
let query: ParsedIRIValues;
|
|
58
|
+
let iriValues: IRIValue;
|
|
59
|
+
let bodyValues: BodyValue;
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
const result = getRequestIRIValues({ iri, action });
|
|
63
|
+
|
|
64
|
+
params = result.pathValues;
|
|
65
|
+
query = result.queryValues;
|
|
66
|
+
iriValues = result.iriValues;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
{
|
|
70
|
+
const result = await getRequestBodyValues({ req, action });
|
|
71
|
+
|
|
72
|
+
bodyValues = result.bodyValues;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const mergedValues: BodyValue & IRIValue = Object.assign(Object.create(null), bodyValues, iriValues);
|
|
76
|
+
|
|
77
|
+
function hasRequiredFields(
|
|
78
|
+
specObject: Record<
|
|
79
|
+
string,
|
|
80
|
+
{
|
|
81
|
+
valueRequired?: boolean;
|
|
82
|
+
valueName?: string;
|
|
83
|
+
}
|
|
84
|
+
>,
|
|
85
|
+
) {
|
|
86
|
+
if (!isPopulatedObject(specObject)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const specValue of Object.values(specObject)) {
|
|
91
|
+
if (specValue.valueRequired && specValue.valueName == null) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function handleValueSpecValue({
|
|
100
|
+
paramName,
|
|
101
|
+
pointer,
|
|
102
|
+
value,
|
|
103
|
+
specValue,
|
|
104
|
+
}: {
|
|
105
|
+
paramName: string;
|
|
106
|
+
pointer: string;
|
|
107
|
+
value: JSONValue;
|
|
108
|
+
specValue: ValueSpec;
|
|
109
|
+
}) {
|
|
110
|
+
if (typeof value === 'undefined' && !specValue.valueRequired) {
|
|
111
|
+
return;
|
|
112
|
+
} else if (value === null && !specValue.valueRequired) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (failsRequiredRequirement(value, specValue)) {
|
|
117
|
+
appendProblemDetailsParam({
|
|
118
|
+
status: 400,
|
|
119
|
+
param: {
|
|
120
|
+
name: paramName,
|
|
121
|
+
reason: `Value required`,
|
|
122
|
+
pointer,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
} else if (failsTypeRequirement(value, specValue)) {
|
|
128
|
+
if (
|
|
129
|
+
specValue.dataType === 'boolean' ||
|
|
130
|
+
specValue.dataType === 'number' ||
|
|
131
|
+
specValue.dataType === 'file'
|
|
132
|
+
) {
|
|
133
|
+
appendProblemDetailsParam({
|
|
134
|
+
status: 400,
|
|
135
|
+
param: {
|
|
136
|
+
name: paramName,
|
|
137
|
+
reason: `Invalid datatype provided when a ${specValue.dataType} is expected`,
|
|
138
|
+
pointer,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
appendProblemDetailsParam({
|
|
143
|
+
status: 400,
|
|
144
|
+
param: {
|
|
145
|
+
name: paramName,
|
|
146
|
+
reason: `Invalid datatype provided`,
|
|
147
|
+
pointer,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
} else if (failsContentTypeRequirement(value, specValue)) {
|
|
154
|
+
appendProblemDetailsParam({
|
|
155
|
+
status: 400,
|
|
156
|
+
param: {
|
|
157
|
+
name: paramName,
|
|
158
|
+
reason: `File does not meet content type requirements for upload`,
|
|
159
|
+
pointer,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
} else if (failsMaxValue(value, specValue)) {
|
|
163
|
+
appendProblemDetailsParam({
|
|
164
|
+
status: 400,
|
|
165
|
+
param: {
|
|
166
|
+
name: paramName,
|
|
167
|
+
reason: `Value too large`,
|
|
168
|
+
pointer,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return null;
|
|
173
|
+
} else if (failsMinValue(value, specValue)) {
|
|
174
|
+
appendProblemDetailsParam({
|
|
175
|
+
status: 400,
|
|
176
|
+
param: {
|
|
177
|
+
name: paramName,
|
|
178
|
+
reason: `Value too small`,
|
|
179
|
+
pointer,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return null;
|
|
184
|
+
} else if (failValueMinLength(value, specValue)) {
|
|
185
|
+
appendProblemDetailsParam({
|
|
186
|
+
status: 400,
|
|
187
|
+
param: {
|
|
188
|
+
name: paramName,
|
|
189
|
+
reason: `Value's length too small`,
|
|
190
|
+
pointer,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return null;
|
|
195
|
+
} else if (failValueMaxLength(value, specValue)) {
|
|
196
|
+
appendProblemDetailsParam({
|
|
197
|
+
status: 400,
|
|
198
|
+
param: {
|
|
199
|
+
name: paramName,
|
|
200
|
+
reason: `Value's length too large`,
|
|
201
|
+
pointer,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
} else if (failsStepValue(value, specValue)) {
|
|
207
|
+
appendProblemDetailsParam({
|
|
208
|
+
status: 400,
|
|
209
|
+
param: {
|
|
210
|
+
name: paramName,
|
|
211
|
+
reason: `Value does not meet step requirements`,
|
|
212
|
+
pointer,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return null;
|
|
217
|
+
} else if (failsPatternValue(value, specValue)) {
|
|
218
|
+
appendProblemDetailsParam({
|
|
219
|
+
status: 400,
|
|
220
|
+
param: {
|
|
221
|
+
name: paramName,
|
|
222
|
+
reason: `Value does not meet pattern requirements`,
|
|
223
|
+
pointer,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return null;
|
|
228
|
+
} else if (failsValidator(value, specValue)) {
|
|
229
|
+
appendProblemDetailsParam({
|
|
230
|
+
status: 400,
|
|
231
|
+
param: {
|
|
232
|
+
name: paramName,
|
|
233
|
+
reason: `Value is not valid`,
|
|
234
|
+
pointer,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const transformer = specValue.transformer as (
|
|
242
|
+
value: JSONValue,
|
|
243
|
+
state: State,
|
|
244
|
+
) => unknown | Promise<unknown>;
|
|
245
|
+
|
|
246
|
+
if (typeof transformer !== 'undefined') {
|
|
247
|
+
// deno-lint-ignore no-async-promise-executor
|
|
248
|
+
transformers[pointer] = new Promise(async (resolve, reject) => {
|
|
249
|
+
try {
|
|
250
|
+
resolve(await transformer(value, state));
|
|
251
|
+
} catch (err) {
|
|
252
|
+
if (err instanceof InvalidActionParamsError) {
|
|
253
|
+
if (typeof httpStatus !== 'number') {
|
|
254
|
+
httpStatus = err.status;
|
|
255
|
+
} else if (httpStatus !== err.status) {
|
|
256
|
+
httpStatus = 400;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
appendProblemDetailsParam({
|
|
260
|
+
param: {
|
|
261
|
+
name: paramName,
|
|
262
|
+
reason: err.message,
|
|
263
|
+
pointer,
|
|
264
|
+
},
|
|
265
|
+
status: 400,
|
|
266
|
+
});
|
|
267
|
+
} else {
|
|
268
|
+
appendProblemDetailsParam({
|
|
269
|
+
param: {
|
|
270
|
+
name: paramName,
|
|
271
|
+
reason: `Failed to cast value`,
|
|
272
|
+
pointer,
|
|
273
|
+
},
|
|
274
|
+
status: 400,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
reject(err);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function handleArraySpecValue({
|
|
289
|
+
paramName,
|
|
290
|
+
pointer,
|
|
291
|
+
value: parentValue,
|
|
292
|
+
specValue,
|
|
293
|
+
}: {
|
|
294
|
+
paramName: string;
|
|
295
|
+
pointer: string;
|
|
296
|
+
value: JSONValue;
|
|
297
|
+
specValue: ArraySpec;
|
|
298
|
+
}): unknown[] | null {
|
|
299
|
+
if (typeof parentValue === 'undefined' && !specValue.valueRequired) {
|
|
300
|
+
return [];
|
|
301
|
+
} else if (parentValue === null && !specValue.valueRequired) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (failsTypeRequirement(parentValue, specValue)) {
|
|
306
|
+
throw new Error('Nope');
|
|
307
|
+
appendProblemDetailsParam({
|
|
308
|
+
param: {
|
|
309
|
+
name: paramName,
|
|
310
|
+
reason: `Value required`,
|
|
311
|
+
pointer,
|
|
312
|
+
},
|
|
313
|
+
status: 400,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const arrayValue = alwaysArray(parentValue);
|
|
320
|
+
|
|
321
|
+
if (failValueMinLength(arrayValue, specValue)) {
|
|
322
|
+
appendProblemDetailsParam({
|
|
323
|
+
param: {
|
|
324
|
+
name: paramName,
|
|
325
|
+
reason: `Value's length too small`,
|
|
326
|
+
pointer,
|
|
327
|
+
},
|
|
328
|
+
status: 400,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return null;
|
|
332
|
+
} else if (failValueMaxLength(arrayValue, specValue)) {
|
|
333
|
+
appendProblemDetailsParam({
|
|
334
|
+
param: {
|
|
335
|
+
name: paramName,
|
|
336
|
+
reason: `Value's length too large`,
|
|
337
|
+
pointer,
|
|
338
|
+
},
|
|
339
|
+
status: 400,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const sanitizedValue: Array<unknown> = [];
|
|
346
|
+
const childSpecValue: ValueSpec = {
|
|
347
|
+
...specValue,
|
|
348
|
+
multipleValues: undefined,
|
|
349
|
+
} as ValueSpec;
|
|
350
|
+
|
|
351
|
+
for (let index = 0; index < arrayValue.length; index++) {
|
|
352
|
+
const value = arrayValue[index];
|
|
353
|
+
|
|
354
|
+
sanitizedValue.push(
|
|
355
|
+
handleValueSpecValue({
|
|
356
|
+
paramName,
|
|
357
|
+
value,
|
|
358
|
+
specValue: childSpecValue,
|
|
359
|
+
pointer: `${pointer}/${index}`,
|
|
360
|
+
}),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return sanitizedValue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function handleObjectSpecValue({
|
|
368
|
+
paramName,
|
|
369
|
+
pointer,
|
|
370
|
+
value: parentValue,
|
|
371
|
+
specValue: parentSpecValue,
|
|
372
|
+
}: {
|
|
373
|
+
paramName: string;
|
|
374
|
+
pointer: string;
|
|
375
|
+
value: JSONValue;
|
|
376
|
+
specValue: ObjectSpec;
|
|
377
|
+
}): Record<string, unknown> | null {
|
|
378
|
+
if (failsTypeRequirement(parentValue, parentSpecValue)) {
|
|
379
|
+
appendProblemDetailsParam({
|
|
380
|
+
param: {
|
|
381
|
+
name: paramName,
|
|
382
|
+
reason: `Value required`,
|
|
383
|
+
pointer,
|
|
384
|
+
},
|
|
385
|
+
status: 400,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return null;
|
|
389
|
+
} else if (!isObject(parentValue)) {
|
|
390
|
+
appendProblemDetailsParam({
|
|
391
|
+
param: {
|
|
392
|
+
name: paramName,
|
|
393
|
+
reason: `Object expected`,
|
|
394
|
+
pointer,
|
|
395
|
+
},
|
|
396
|
+
status: 400,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (
|
|
403
|
+
const [paramName, specValue] of Object.entries(
|
|
404
|
+
parentSpecValue.properties,
|
|
405
|
+
)
|
|
406
|
+
) {
|
|
407
|
+
if (specValue.valueRequired && isNil(parentValue[paramName])) {
|
|
408
|
+
appendProblemDetailsParam({
|
|
409
|
+
param: {
|
|
410
|
+
name: paramName,
|
|
411
|
+
reason: `Value required`,
|
|
412
|
+
pointer: `${pointer}/${paramName}`,
|
|
413
|
+
},
|
|
414
|
+
status: 400,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const sanitizedValue: Record<string, unknown> = {};
|
|
420
|
+
const specValue = parentSpecValue.properties[paramName];
|
|
421
|
+
|
|
422
|
+
if (typeof specValue !== 'undefined') {
|
|
423
|
+
for (const [paramNameX, value] of Object.entries(parentValue)) {
|
|
424
|
+
const internalName = getInternalName({
|
|
425
|
+
paramName,
|
|
426
|
+
specValue,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
sanitizedValue[internalName] = handleSpecValue({
|
|
430
|
+
paramName: paramNameX,
|
|
431
|
+
value,
|
|
432
|
+
specValue,
|
|
433
|
+
pointer: `${pointer}/${internalName}`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return sanitizedValue;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function handleObjectArraySpecValue({
|
|
442
|
+
paramName,
|
|
443
|
+
pointer: parentPointer,
|
|
444
|
+
value: parentValue,
|
|
445
|
+
specValue,
|
|
446
|
+
}: {
|
|
447
|
+
paramName: string;
|
|
448
|
+
pointer: string;
|
|
449
|
+
value: JSONValue;
|
|
450
|
+
specValue: ObjectArraySpec;
|
|
451
|
+
}): unknown[] | null {
|
|
452
|
+
if (failsTypeRequirement(parentValue, specValue)) {
|
|
453
|
+
appendProblemDetailsParam({
|
|
454
|
+
param: {
|
|
455
|
+
name: paramName,
|
|
456
|
+
reason: `Value required`,
|
|
457
|
+
pointer: parentPointer,
|
|
458
|
+
},
|
|
459
|
+
status: 400,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const arrayValue = alwaysArray(parentValue);
|
|
466
|
+
|
|
467
|
+
if (failValueMinLength(arrayValue, specValue)) {
|
|
468
|
+
appendProblemDetailsParam({
|
|
469
|
+
param: {
|
|
470
|
+
name: paramName,
|
|
471
|
+
reason: `Value's length too small`,
|
|
472
|
+
pointer: parentPointer,
|
|
473
|
+
},
|
|
474
|
+
status: 400,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
return null;
|
|
478
|
+
} else if (failValueMaxLength(arrayValue, specValue)) {
|
|
479
|
+
appendProblemDetailsParam({
|
|
480
|
+
param: {
|
|
481
|
+
name: paramName,
|
|
482
|
+
reason: `Value's length too large`,
|
|
483
|
+
pointer: parentPointer,
|
|
484
|
+
},
|
|
485
|
+
status: 400,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const objectSpecValue: ObjectSpec = {
|
|
492
|
+
...specValue,
|
|
493
|
+
multipleValues: undefined,
|
|
494
|
+
} as ObjectSpec;
|
|
495
|
+
const sanitizedArrayValue: Array<unknown> = [];
|
|
496
|
+
|
|
497
|
+
for (let index = 0; index < arrayValue.length; index++) {
|
|
498
|
+
const value = arrayValue[index];
|
|
499
|
+
// deno-lint-ignore no-explicit-any
|
|
500
|
+
const sanitizedValue: Record<string, any> = {};
|
|
501
|
+
const pointer = `${parentPointer}/${index}`;
|
|
502
|
+
|
|
503
|
+
for (
|
|
504
|
+
const [paramName, objectSpec] of Object.entries(
|
|
505
|
+
objectSpecValue.properties,
|
|
506
|
+
)
|
|
507
|
+
) {
|
|
508
|
+
const internalName = getInternalName({
|
|
509
|
+
paramName,
|
|
510
|
+
specValue: objectSpec,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
if (!isPopulatedObject<JSONValue>(value)) {
|
|
514
|
+
appendProblemDetailsParam({
|
|
515
|
+
status: 400,
|
|
516
|
+
param: {
|
|
517
|
+
name: paramName,
|
|
518
|
+
reason: `Object expected`,
|
|
519
|
+
pointer,
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
continue;
|
|
524
|
+
} else {
|
|
525
|
+
sanitizedValue[internalName] = handleSpecValue({
|
|
526
|
+
paramName,
|
|
527
|
+
value: value[paramName],
|
|
528
|
+
specValue: objectSpec,
|
|
529
|
+
pointer: `${pointer}/${internalName}`,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
sanitizedArrayValue.push(sanitizedValue);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return sanitizedArrayValue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function handleSpecValue({
|
|
541
|
+
paramName,
|
|
542
|
+
pointer,
|
|
543
|
+
value,
|
|
544
|
+
specValue,
|
|
545
|
+
}: {
|
|
546
|
+
paramName: string;
|
|
547
|
+
pointer: string;
|
|
548
|
+
value: JSONValue;
|
|
549
|
+
// deno-lint-ignore no-explicit-any
|
|
550
|
+
specValue: PropertySpec<any, 'STOP'>;
|
|
551
|
+
}): unknown | Array<unknown> | Record<string, unknown> | null {
|
|
552
|
+
if (isObjectArraySpec(specValue)) {
|
|
553
|
+
return handleObjectArraySpecValue({
|
|
554
|
+
paramName,
|
|
555
|
+
pointer,
|
|
556
|
+
value,
|
|
557
|
+
specValue,
|
|
558
|
+
});
|
|
559
|
+
} else if (isObjectSpec(specValue)) {
|
|
560
|
+
return handleObjectSpecValue({
|
|
561
|
+
paramName,
|
|
562
|
+
pointer,
|
|
563
|
+
value,
|
|
564
|
+
specValue,
|
|
565
|
+
});
|
|
566
|
+
} else if (isArraySpec(specValue)) {
|
|
567
|
+
return handleArraySpecValue({
|
|
568
|
+
paramName,
|
|
569
|
+
pointer,
|
|
570
|
+
value,
|
|
571
|
+
specValue,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return handleValueSpecValue({
|
|
576
|
+
paramName,
|
|
577
|
+
pointer,
|
|
578
|
+
value,
|
|
579
|
+
specValue,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (hasRequiredFields(spec) && !isPopulatedObject(mergedValues)) {
|
|
584
|
+
throw new ProblemDetailsError(
|
|
585
|
+
httpStatus || 400,
|
|
586
|
+
{
|
|
587
|
+
title: `This action requires a request body`,
|
|
588
|
+
},
|
|
589
|
+
);
|
|
590
|
+
} else if (!isPopulatedObject(mergedValues)) {
|
|
591
|
+
return {
|
|
592
|
+
params,
|
|
593
|
+
query,
|
|
594
|
+
payload: payload as unknown as ActionPayload<Spec>,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
for (
|
|
599
|
+
const [paramName, specItem] of Object.entries(
|
|
600
|
+
spec as unknown as Record<string, SpecValue>,
|
|
601
|
+
)
|
|
602
|
+
) {
|
|
603
|
+
if (specItem.valueRequired && isObject(mergedValues) && isNil(mergedValues[paramName])) {
|
|
604
|
+
appendProblemDetailsParam({
|
|
605
|
+
param: {
|
|
606
|
+
name: paramName,
|
|
607
|
+
reason: `Value required`,
|
|
608
|
+
pointer: `#/${paramName}`,
|
|
609
|
+
},
|
|
610
|
+
status: 400,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
for (const [paramName, value] of Object.entries(mergedValues)) {
|
|
616
|
+
if (!Object.hasOwn(spec, paramName)) {
|
|
617
|
+
console.log(spec);
|
|
618
|
+
throw new ProblemDetailsError(
|
|
619
|
+
httpStatus || 400,
|
|
620
|
+
{
|
|
621
|
+
title: `Invalid request`,
|
|
622
|
+
errors: [
|
|
623
|
+
{
|
|
624
|
+
name: paramName,
|
|
625
|
+
reason: 'Unexpected value',
|
|
626
|
+
pointer: `#/${paramName}`,
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
},
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const specValue = spec[paramName];
|
|
634
|
+
|
|
635
|
+
if (specValue.readonlyValue) {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const internalName = getInternalName({
|
|
639
|
+
paramName,
|
|
640
|
+
specValue,
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
payload[internalName as keyof ActionPayload<Spec>] = handleSpecValue({
|
|
644
|
+
paramName,
|
|
645
|
+
pointer: `#/${internalName}`,
|
|
646
|
+
value,
|
|
647
|
+
specValue,
|
|
648
|
+
}) as ActionPayload<Spec>[keyof ActionPayload<Spec>];
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const promises: Array<Promise<void>> = [];
|
|
652
|
+
|
|
653
|
+
if (refs.problemDetails) {
|
|
654
|
+
throw new ProblemDetailsError(
|
|
655
|
+
refs.httpStatus || 400,
|
|
656
|
+
refs.problemDetails,
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
for (const [pointer, transformer] of Object.entries(transformers)) {
|
|
661
|
+
promises.push(
|
|
662
|
+
// deno-lint-ignore no-async-promise-executor
|
|
663
|
+
new Promise<void>(async (resolve) => {
|
|
664
|
+
try {
|
|
665
|
+
const value = await transformer;
|
|
666
|
+
|
|
667
|
+
JsonPointer.set(payload, pointer, value, true);
|
|
668
|
+
// deno-lint-ignore no-empty
|
|
669
|
+
} catch {}
|
|
670
|
+
|
|
671
|
+
resolve();
|
|
672
|
+
}),
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
await Promise.all(promises);
|
|
677
|
+
|
|
678
|
+
if (refs.problemDetails) {
|
|
679
|
+
throw new ProblemDetailsError(
|
|
680
|
+
refs.httpStatus || 400,
|
|
681
|
+
refs.problemDetails,
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return {
|
|
686
|
+
params,
|
|
687
|
+
query,
|
|
688
|
+
payload: payload as unknown as ActionPayload<Spec>,
|
|
689
|
+
};
|
|
690
|
+
}
|