@owlmeans/api 0.1.18-rc.46 → 0.1.18-rc.48
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/README.md +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/api/SKILL.md +22 -20
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/service.js +6 -6
- package/build/service.js.map +1 -1
- package/build/status/helper.d.ts +4 -0
- package/build/status/helper.d.ts.map +1 -0
- package/build/status/helper.js +49 -0
- package/build/status/helper.js.map +1 -0
- package/build/status/index.d.ts +3 -22
- package/build/status/index.d.ts.map +1 -1
- package/build/status/index.js +4 -61
- package/build/status/index.js.map +1 -1
- package/build/status/types.d.ts +24 -0
- package/build/status/types.d.ts.map +1 -1
- package/build/utils/body/types.d.ts +36 -0
- package/build/utils/body/types.d.ts.map +1 -0
- package/build/utils/{types.js.map → body/types.js.map} +1 -1
- package/build/utils/body.d.ts +3 -38
- package/build/utils/body.d.ts.map +1 -1
- package/build/utils/body.js +49 -66
- package/build/utils/body.js.map +1 -1
- package/build/utils/handler.d.ts +1 -1
- package/build/utils/handler.d.ts.map +1 -1
- package/build/utils/handler.js.map +1 -1
- package/build/utils/index.d.ts +2 -1
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -1
- package/build/utils/index.js.map +1 -1
- package/build/utils/response/types.d.ts +15 -0
- package/build/utils/response/types.d.ts.map +1 -0
- package/build/utils/response/types.js +2 -0
- package/build/utils/response/types.js.map +1 -0
- package/build/utils/response.d.ts +4 -0
- package/build/utils/response.d.ts.map +1 -0
- package/build/utils/response.js +99 -0
- package/build/utils/response.js.map +1 -0
- package/package.json +11 -11
- package/src/index.ts +2 -2
- package/src/service.ts +6 -6
- package/src/status/helper.ts +63 -0
- package/src/status/index.ts +5 -76
- package/src/status/types.ts +25 -0
- package/src/utils/body/types.ts +37 -0
- package/src/utils/body.ts +50 -67
- package/src/utils/index.ts +2 -1
- package/src/utils/response/types.ts +15 -0
- package/src/utils/response.ts +117 -0
- package/tests/body.spec.ts +29 -29
- package/tests/handler.spec.ts +45 -45
- package/build/utils/types.d.ts +0 -11
- package/build/utils/types.d.ts.map +0 -1
- package/src/utils/handler.ts +0 -110
- package/src/utils/types.ts +0 -12
- /package/build/utils/{types.js → body/types.js} +0 -0
package/build/utils/body.js
CHANGED
|
@@ -2,72 +2,55 @@ import qs from 'qs';
|
|
|
2
2
|
import { RouteMethod } from '@owlmeans/route';
|
|
3
3
|
import { FORM_CONTENT_TYPE } from './consts.local.js';
|
|
4
4
|
import { JSON_CONTENT_TYPE } from './consts.js';
|
|
5
|
-
|
|
6
|
-
const contentTypeOf = (headers) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* supplied for a `POST` that names none — becomes JSON text ({@link jsonScalarBody}).
|
|
52
|
-
* - Anything else — objects, arrays, a scalar under another content type — goes to axios as it is,
|
|
53
|
-
* which serializes an object or an array as JSON.
|
|
54
|
-
*/
|
|
55
|
-
const requestBodyOf = (body, headers, method, schema) => {
|
|
56
|
-
const type = contentTypeOf(headers);
|
|
57
|
-
if (body != null && type?.includes(FORM_CONTENT_TYPE) === true) {
|
|
58
|
-
return { data: qs.stringify(body), verbatim: false };
|
|
59
|
-
}
|
|
60
|
-
if (!isScalar(body)) {
|
|
5
|
+
export const createBodyUtils = () => {
|
|
6
|
+
const contentTypeOf = (headers) => {
|
|
7
|
+
const value = Object.entries(headers ?? {}).find(([key]) => key.toLowerCase() === 'content-type')?.[1];
|
|
8
|
+
if (value == null || value === '') {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return Array.isArray(value) ? value.join(', ') : `${value}`;
|
|
12
|
+
};
|
|
13
|
+
const isJsonContentType = (type) => type != null && /^application\/(?:[^\s;]+\+)?json\s*(?:;|$)/i.test(type.trim());
|
|
14
|
+
/** A JSON value no serializer quotes for us: a string, a number or a boolean. */
|
|
15
|
+
const isScalar = (value) => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
16
|
+
/** Does the entrypoint's body schema declare a string? */
|
|
17
|
+
const stringContract = (schema) => {
|
|
18
|
+
const type = schema?.type;
|
|
19
|
+
return (Array.isArray(type) ? type : [type]).includes('string');
|
|
20
|
+
};
|
|
21
|
+
const parse = (text) => {
|
|
22
|
+
try {
|
|
23
|
+
return { json: true, value: JSON.parse(text) };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { json: false };
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const jsonScalarBody = (body, schema) => {
|
|
30
|
+
if (typeof body !== 'string') {
|
|
31
|
+
return JSON.stringify(body);
|
|
32
|
+
}
|
|
33
|
+
const parsed = parse(body);
|
|
34
|
+
const serialized = parsed.json && (!stringContract(schema) || typeof parsed.value === 'string');
|
|
35
|
+
return serialized ? body : JSON.stringify(body);
|
|
36
|
+
};
|
|
37
|
+
const requestBodyOf = (body, headers, method, schema) => {
|
|
38
|
+
const type = contentTypeOf(headers);
|
|
39
|
+
if (body != null && type?.includes(FORM_CONTENT_TYPE) === true) {
|
|
40
|
+
return { data: qs.stringify(body), verbatim: false };
|
|
41
|
+
}
|
|
42
|
+
if (!isScalar(body)) {
|
|
43
|
+
return { data: body, verbatim: false };
|
|
44
|
+
}
|
|
45
|
+
if (type == null && method === RouteMethod.POST) {
|
|
46
|
+
return { data: jsonScalarBody(body, schema), contentType: JSON_CONTENT_TYPE, verbatim: true };
|
|
47
|
+
}
|
|
48
|
+
if (isJsonContentType(type)) {
|
|
49
|
+
return { data: jsonScalarBody(body, schema), verbatim: true };
|
|
50
|
+
}
|
|
61
51
|
return { data: body, verbatim: false };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { data: jsonScalarBody(body, schema), contentType: JSON_CONTENT_TYPE, verbatim: true };
|
|
65
|
-
}
|
|
66
|
-
if (isJsonContentType(type)) {
|
|
67
|
-
return { data: jsonScalarBody(body, schema), verbatim: true };
|
|
68
|
-
}
|
|
69
|
-
return { data: body, verbatim: false };
|
|
52
|
+
};
|
|
53
|
+
return { contentTypeOf, isJsonContentType, jsonScalarBody, requestBodyOf };
|
|
70
54
|
};
|
|
71
|
-
export const
|
|
72
|
-
export const bodyHelper = createBodyHelper();
|
|
55
|
+
export const bodyUtils = createBodyUtils();
|
|
73
56
|
//# sourceMappingURL=body.js.map
|
package/build/utils/body.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/utils/body.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/C,
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/utils/body.ts"],"names":[],"mappings":"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"}
|
package/build/utils/handler.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AbstractResponse } from '@owlmeans/entrypoint';
|
|
2
2
|
import type { AxiosResponse } from 'axios';
|
|
3
3
|
import { ApiClientError } from '../errors.js';
|
|
4
4
|
export declare const processResponse: (response: AxiosResponse, reply: AbstractResponse<any>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAM1C,OAAO,EAAE,cAAc,EAAuD,MAAM,cAAc,CAAA;AAOlG,eAAO,MAAM,eAAe,aAAc,aAAa,SAAS,gBAAgB,CAAC,GAAG,CAAC,SAqBpF,CAAA;AAkCD;;;;GAIG;AACH,eAAO,MAAM,WAAW,WAAY,MAAM,eAAe,MAAM,KAAG,cAUjE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"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"}
|
package/build/utils/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,mBAAmB,qBAAqB,CAAA;AACxC,cAAc,eAAe,CAAA"}
|
package/build/utils/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './response.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/build/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AbstractResponse } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { AxiosResponse } from 'axios';
|
|
3
|
+
import type { ApiClientError } from '../../errors.js';
|
|
4
|
+
/** Turns an axios answer into the entrypoint reply: a resolved value or a typed failure. */
|
|
5
|
+
export interface ResponseUtils {
|
|
6
|
+
/** Resolves `reply` with a 2xx answer's value, or rejects it with the failure of any other status. */
|
|
7
|
+
processResponse: (response: AxiosResponse, reply: AbstractResponse<any>) => void;
|
|
8
|
+
/**
|
|
9
|
+
* The typed error of a bare status: `ServerCrashedError` (500), `ServerAuthError` (401),
|
|
10
|
+
* `ApiClientError('forbidden[:<id>]')` (403), `ApiStatusError` (`api:client:status:<n>[:<id>]`)
|
|
11
|
+
* for every other one.
|
|
12
|
+
*/
|
|
13
|
+
statusError: (status: number, incidentId?: string) => ApiClientError;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/response/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC5B,sGAAsG;IACtG,eAAe,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IAChF;;;;OAIG;IACH,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,cAAc,CAAA;CACrE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/response/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/utils/response.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,eAAO,MAAM,mBAAmB,QAAO,aAqGtC,CAAA;AAED,eAAO,MAAM,aAAa,eAAwB,CAAA"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { EntrypointOutcome } from '@owlmeans/entrypoint';
|
|
2
|
+
import { ACCEPTED, CREATED, FINISHED, FORBIDDEN_ERROR, INCIDENT_ID_HEADER, DENIAL_KIND_HEADER, ACCESS_DENIED_KIND, OK, SERVER_ERROR, UNAUTHORIZED_ERROR, } from '../consts.js';
|
|
3
|
+
import { ResilientError } from '@owlmeans/error';
|
|
4
|
+
import { ApiClientError, ApiStatusError, ServerAuthError, ServerCrashedError } from '../errors.js';
|
|
5
|
+
import { apiStatusHelper } from '../status/helper.js';
|
|
6
|
+
import { logger } from '@owlmeans/log';
|
|
7
|
+
export const createResponseUtils = () => {
|
|
8
|
+
const log = logger('api');
|
|
9
|
+
const processResponse = (response, reply) => {
|
|
10
|
+
switch (response.status) {
|
|
11
|
+
case OK:
|
|
12
|
+
reply.resolve(response.data, EntrypointOutcome.Ok);
|
|
13
|
+
return;
|
|
14
|
+
case ACCEPTED:
|
|
15
|
+
reply.resolve(response.data, EntrypointOutcome.Accepted);
|
|
16
|
+
return;
|
|
17
|
+
case CREATED:
|
|
18
|
+
return processEmptyResponse(response, reply, EntrypointOutcome.Created);
|
|
19
|
+
case FINISHED:
|
|
20
|
+
return processEmptyResponse(response, reply, EntrypointOutcome.Finished);
|
|
21
|
+
default:
|
|
22
|
+
// A refusal is ordinary traffic (401, 403, 409, 428): debug, and never the headers.
|
|
23
|
+
log.debug('Request answered with a failure', {
|
|
24
|
+
status: response.status, statusText: response.statusText, url: response.config?.url,
|
|
25
|
+
incidentId: headerOf(response.headers, INCIDENT_ID_HEADER),
|
|
26
|
+
});
|
|
27
|
+
reply.reject(failureOf(response));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The error a non-2xx answer rejects with — always carrying the HTTP status and, when the server
|
|
32
|
+
* sent one, its incident id.
|
|
33
|
+
*
|
|
34
|
+
* A development body is the marshaled `ResilientError`: it is rebuilt into its own class and
|
|
35
|
+
* stamped with `responseStatus`. Anything else — the bare incident id of a production body, a
|
|
36
|
+
* proxy's HTML, a framework's JSON — becomes the status error of {@link statusError}.
|
|
37
|
+
*/
|
|
38
|
+
const failureOf = (response) => {
|
|
39
|
+
const incidentId = headerOf(response.headers, INCIDENT_ID_HEADER)
|
|
40
|
+
?? (apiStatusHelper.isIncidentBody(response.data) ? response.data.trim() : undefined);
|
|
41
|
+
const denialKind = response.status === FORBIDDEN_ERROR
|
|
42
|
+
&& headerOf(response.headers, DENIAL_KIND_HEADER) === ACCESS_DENIED_KIND
|
|
43
|
+
? ACCESS_DENIED_KIND : undefined;
|
|
44
|
+
if (typeof response.data === 'string' && response.data.includes(ResilientError.separator)) {
|
|
45
|
+
try {
|
|
46
|
+
const error = ResilientError.ensure(response.data, true);
|
|
47
|
+
error.incidentId ??= incidentId;
|
|
48
|
+
Object.assign(error, {
|
|
49
|
+
responseStatus: response.status, denialKind,
|
|
50
|
+
});
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
log.warn('Server returned an unrecognizable text error', { status: response.status, body: response.data });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return Object.assign(statusError(response.status, incidentId), { denialKind });
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* The typed error of a bare status: `ServerCrashedError` (500), `ServerAuthError` (401),
|
|
61
|
+
* `ApiClientError('forbidden[:<id>]')` (403), `ApiStatusError` (`api:client:status:<n>[:<id>]`)
|
|
62
|
+
* for every other one.
|
|
63
|
+
*/
|
|
64
|
+
const statusError = (status, incidentId) => {
|
|
65
|
+
const error = status === SERVER_ERROR ? new ServerCrashedError(incidentId)
|
|
66
|
+
: status === UNAUTHORIZED_ERROR ? new ServerAuthError(incidentId)
|
|
67
|
+
: status === FORBIDDEN_ERROR ? new ApiClientError(incidentId != null ? `forbidden:${incidentId}` : 'forbidden')
|
|
68
|
+
: new ApiStatusError(status, incidentId);
|
|
69
|
+
if (incidentId != null) {
|
|
70
|
+
error.incidentId = incidentId;
|
|
71
|
+
}
|
|
72
|
+
return Object.assign(error, { responseStatus: status });
|
|
73
|
+
};
|
|
74
|
+
/** One response header, from an `AxiosHeaders` instance or a plain header object alike. */
|
|
75
|
+
const headerOf = (headers, name) => {
|
|
76
|
+
if (headers == null || typeof headers !== 'object') {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const get = headers.get;
|
|
80
|
+
const viaGet = typeof get === 'function' ? get.call(headers, name) : undefined;
|
|
81
|
+
const value = viaGet ?? Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase())?.[1];
|
|
82
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
83
|
+
return typeof first === 'string' && first.trim() !== '' ? first.trim() : undefined;
|
|
84
|
+
};
|
|
85
|
+
const processEmptyResponse = (response, reply, outcome) => {
|
|
86
|
+
if (response.data != null && response.data != '') {
|
|
87
|
+
reply.resolve(response.data, outcome);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (response.headers.toJSON != null && typeof response.headers.toJSON === 'function') {
|
|
91
|
+
reply.resolve(response.headers.toJSON(), outcome);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
reply.resolve(response.headers, outcome);
|
|
95
|
+
};
|
|
96
|
+
return { processResponse, statusError };
|
|
97
|
+
};
|
|
98
|
+
export const responseUtils = createResponseUtils();
|
|
99
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/utils/response.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/api",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.48",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -29,21 +29,21 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/client-config": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/config": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
37
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
38
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
39
|
-
"@owlmeans/log": "^0.1.18-rc.
|
|
40
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
32
|
+
"@owlmeans/auth-common": "^0.1.18-rc.48",
|
|
33
|
+
"@owlmeans/client-config": "^0.1.18-rc.44",
|
|
34
|
+
"@owlmeans/client-route": "^0.1.18-rc.49",
|
|
35
|
+
"@owlmeans/config": "^0.1.18-rc.44",
|
|
36
|
+
"@owlmeans/context": "^0.1.18-rc.34",
|
|
37
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.41",
|
|
38
|
+
"@owlmeans/error": "^0.1.18-rc.38",
|
|
39
|
+
"@owlmeans/log": "^0.1.18-rc.2",
|
|
40
|
+
"@owlmeans/route": "^0.1.18-rc.35",
|
|
41
41
|
"axios": "^1.20.0",
|
|
42
42
|
"qs": "^6.16.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@owlmeans/dep-config": "workspace:*",
|
|
46
|
-
"@owlmeans/resource": "^0.1.18-rc.
|
|
46
|
+
"@owlmeans/resource": "^0.1.18-rc.40",
|
|
47
47
|
"@types/qs": "^6.15.1",
|
|
48
48
|
"nodemon": "^3.1.14",
|
|
49
49
|
"typescript": "^7.0.2"
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from './consts.js'
|
|
|
3
3
|
export * from './service.js'
|
|
4
4
|
export type * from './types.js'
|
|
5
5
|
export {
|
|
6
|
-
httpStatusOf,
|
|
6
|
+
httpStatusOf, apiStatusHelper, createApiStatusHelper, parseClientMarker, API_CLIENT_MARKER, API_STATUS_MARKER,
|
|
7
7
|
} from './status/index.js'
|
|
8
|
-
export type { ClientMarker } from './status/index.js'
|
|
8
|
+
export type { ClientMarker, ApiStatusHelper } from './status/index.js'
|
|
9
9
|
export type { ResponseStatusCarrier } from './status/types.js'
|
package/src/service.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { assertContext, createService } from '@owlmeans/context'
|
|
2
|
-
import {
|
|
2
|
+
import { clientRouteHelper } from '@owlmeans/client-route'
|
|
3
3
|
import type { ApiClient } from './types.js'
|
|
4
4
|
import axios, { AxiosHeaders } from 'axios'
|
|
5
5
|
import type { AxiosRequestTransformer } from 'axios'
|
|
6
6
|
import type { CommonEntrypoint } from '@owlmeans/entrypoint'
|
|
7
7
|
import { DEFAULT_ALIAS, UNAUTHORIZED_ERROR } from './consts.js'
|
|
8
|
-
import {
|
|
8
|
+
import { responseUtils } from './utils/response.js'
|
|
9
9
|
import { makeSecurityHelper } from '@owlmeans/config'
|
|
10
10
|
import { AUTH_HEADER, DEF_AUTH_SRV, TOKEN_UPDATE, type AuthService } from '@owlmeans/auth-common'
|
|
11
11
|
import type { Config, Context } from './types.local.js'
|
|
12
|
-
import {
|
|
12
|
+
import { bodyUtils } from './utils/body.js'
|
|
13
13
|
|
|
14
14
|
/** Whether a request carried an authentication header of its own. */
|
|
15
15
|
const presented = (headers?: Record<string, unknown>): boolean =>
|
|
@@ -28,7 +28,7 @@ export const createApiService = (alias: string = DEFAULT_ALIAS): ApiClient => {
|
|
|
28
28
|
const module = context.entrypoint<CommonEntrypoint>(request.alias)
|
|
29
29
|
const route = module.route.route
|
|
30
30
|
let path = module.path()
|
|
31
|
-
const params = extractParams(path)
|
|
31
|
+
const params = clientRouteHelper.extractParams(path)
|
|
32
32
|
path = params.reduce((path, param) => {
|
|
33
33
|
type Key = keyof typeof request.params
|
|
34
34
|
if (request.params[param as Key] == null) {
|
|
@@ -48,7 +48,7 @@ export const createApiService = (alias: string = DEFAULT_ALIAS): ApiClient => {
|
|
|
48
48
|
|
|
49
49
|
// A scalar JSON body (a string, a number, a boolean) is serialized here, so the server parses
|
|
50
50
|
// back the value the caller sent; objects and arrays are left to axios.
|
|
51
|
-
const body =
|
|
51
|
+
const body = bodyUtils.requestBodyOf(request.body, request.headers, route.method, module.filter?.body)
|
|
52
52
|
const transformer: AxiosRequestTransformer | undefined = body.verbatim ? data => data : undefined
|
|
53
53
|
const requestHeaders = body.contentType != null
|
|
54
54
|
? { ...request.headers, 'content-type': body.contentType }
|
|
@@ -94,7 +94,7 @@ export const createApiService = (alias: string = DEFAULT_ALIAS): ApiClient => {
|
|
|
94
94
|
await context.service<AuthService>(DEF_AUTH_SRV).update(undefined)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
processResponse(response, reply)
|
|
97
|
+
responseUtils.processResponse(response, reply)
|
|
98
98
|
|
|
99
99
|
return [reply.error ?? reply.value, reply.outcome] as any
|
|
100
100
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error'
|
|
2
|
+
import { ApiClientError } from '../errors.js'
|
|
3
|
+
import { parseClientMarker } from './marker.js'
|
|
4
|
+
import { ACCESS_DENIED_KIND } from '../consts.js'
|
|
5
|
+
import { UUID } from './consts.local.js'
|
|
6
|
+
import type { ApiStatusHelper, ClientMarker, ResponseStatusCarrier } from './types.js'
|
|
7
|
+
|
|
8
|
+
export const createApiStatusHelper = (): ApiStatusHelper => {
|
|
9
|
+
const isAccessDenied = (error: unknown): boolean =>
|
|
10
|
+
httpStatusOf(error) === 403
|
|
11
|
+
&& typeof error === 'object' && error != null
|
|
12
|
+
&& (error as ResponseStatusCarrier).denialKind === ACCESS_DENIED_KIND
|
|
13
|
+
|
|
14
|
+
const httpStatus = (value: unknown): number | null =>
|
|
15
|
+
typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599 ? value : null
|
|
16
|
+
|
|
17
|
+
/** The first `api:client:*` marker in an error's `message`, else in its `type`. */
|
|
18
|
+
const markerOf = (error: object): ClientMarker | null => {
|
|
19
|
+
const { message, type } = error as { message?: unknown, type?: unknown }
|
|
20
|
+
|
|
21
|
+
return (typeof message === 'string' ? parseClientMarker(message) : null)
|
|
22
|
+
?? (typeof type === 'string' ? parseClientMarker(type) : null)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const httpStatusOf = (error: unknown): number | null => {
|
|
26
|
+
if (error == null || typeof error !== 'object') {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
const stamped = httpStatus((error as ResponseStatusCarrier).responseStatus)
|
|
30
|
+
if (stamped != null) {
|
|
31
|
+
return stamped
|
|
32
|
+
}
|
|
33
|
+
if (error instanceof ApiClientError && httpStatus(error.status) != null) {
|
|
34
|
+
return error.status!
|
|
35
|
+
}
|
|
36
|
+
const declaration = error.constructor as { httpStatus?: unknown, allowServerErrorStatus?: unknown } | undefined
|
|
37
|
+
const declared = httpStatus(declaration?.httpStatus)
|
|
38
|
+
if (declared != null && (declared < 500 || declaration?.allowServerErrorStatus === true)) {
|
|
39
|
+
return declared
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return markerOf(error)?.status ?? null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const incidentIdOf = (error: unknown): string | null => {
|
|
46
|
+
if (error == null || typeof error !== 'object') {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
const own = (error as { incidentId?: unknown }).incidentId
|
|
50
|
+
if (typeof own === 'string' && own !== '') {
|
|
51
|
+
return own
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return markerOf(error)?.incidentId ?? null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const isIncidentBody = (data: unknown): data is string =>
|
|
58
|
+
typeof data === 'string' && !data.includes(ResilientError.separator) && UUID.test(data.trim())
|
|
59
|
+
|
|
60
|
+
return { isAccessDenied, httpStatusOf, incidentIdOf, isIncidentBody }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const apiStatusHelper = createApiStatusHelper()
|
package/src/status/index.ts
CHANGED
|
@@ -1,82 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ApiClientError } from '../errors.js'
|
|
3
|
-
import { parseClientMarker } from './marker.js'
|
|
1
|
+
import { apiStatusHelper } from './helper.js'
|
|
4
2
|
|
|
5
3
|
export { INCIDENT_ID_HEADER } from '../consts.js'
|
|
6
4
|
export { DENIAL_KIND_HEADER, ACCESS_DENIED_KIND } from '../consts.js'
|
|
7
|
-
import { ACCESS_DENIED_KIND } from '../consts.js'
|
|
8
|
-
import { UUID } from './consts.local.js'
|
|
9
|
-
import type { ResponseStatusCarrier } from './types.js'
|
|
10
5
|
export { parseClientMarker } from './marker.js'
|
|
11
6
|
export { API_CLIENT_MARKER, API_STATUS_MARKER } from './consts.js'
|
|
12
|
-
export type { ClientMarker } from './types.js'
|
|
7
|
+
export type { ClientMarker, ResponseStatusCarrier, ApiStatusHelper } from './types.js'
|
|
8
|
+
export { apiStatusHelper, createApiStatusHelper } from './helper.js'
|
|
13
9
|
|
|
14
|
-
/**
|
|
15
|
-
export const
|
|
16
|
-
httpStatusOf(error) === 403
|
|
17
|
-
&& typeof error === 'object' && error != null
|
|
18
|
-
&& (error as ResponseStatusCarrier).denialKind === ACCESS_DENIED_KIND
|
|
19
|
-
|
|
20
|
-
const httpStatus = (value: unknown): number | null =>
|
|
21
|
-
typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599 ? value : null
|
|
22
|
-
|
|
23
|
-
/** The first `api:client:*` marker in an error's `message`, else in its `type`. */
|
|
24
|
-
const markerOf = (error: object) => {
|
|
25
|
-
const { message, type } = error as { message?: unknown, type?: unknown }
|
|
26
|
-
|
|
27
|
-
return (typeof message === 'string' ? parseClientMarker(message) : null)
|
|
28
|
-
?? (typeof type === 'string' ? parseClientMarker(type) : null)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The HTTP status an error answers to, or `null` when nothing states one.
|
|
33
|
-
*
|
|
34
|
-
* Read in order: the status the API client stamped on the error it rebuilt from a response
|
|
35
|
-
* (`responseStatus`); the status an `ApiClientError` parsed from its marker; the `httpStatus` its
|
|
36
|
-
* class declares — a 4xx always, a 5xx only with `allowServerErrorStatus`, exactly what an
|
|
37
|
-
* `@owlmeans/server-api` boundary answers with; finally an `api:client:*` marker in the message or
|
|
38
|
-
* the type of anything else. This is what lets a browser recognise a refusal from its status
|
|
39
|
-
* alone when a production body carries nothing but an incident id.
|
|
40
|
-
*/
|
|
41
|
-
export const httpStatusOf = (error: unknown): number | null => {
|
|
42
|
-
if (error == null || typeof error !== 'object') {
|
|
43
|
-
return null
|
|
44
|
-
}
|
|
45
|
-
const stamped = httpStatus((error as ResponseStatusCarrier).responseStatus)
|
|
46
|
-
if (stamped != null) {
|
|
47
|
-
return stamped
|
|
48
|
-
}
|
|
49
|
-
if (error instanceof ApiClientError && httpStatus(error.status) != null) {
|
|
50
|
-
return error.status!
|
|
51
|
-
}
|
|
52
|
-
const declaration = error.constructor as { httpStatus?: unknown, allowServerErrorStatus?: unknown } | undefined
|
|
53
|
-
const declared = httpStatus(declaration?.httpStatus)
|
|
54
|
-
if (declared != null && (declared < 500 || declaration?.allowServerErrorStatus === true)) {
|
|
55
|
-
return declared
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return markerOf(error)?.status ?? null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** The server's incident id carried by an error — its own field, else its marker — or `null`. */
|
|
62
|
-
export const incidentIdOf = (error: unknown): string | null => {
|
|
63
|
-
if (error == null || typeof error !== 'object') {
|
|
64
|
-
return null
|
|
65
|
-
}
|
|
66
|
-
const own = (error as { incidentId?: unknown }).incidentId
|
|
67
|
-
if (typeof own === 'string' && own !== '') {
|
|
68
|
-
return own
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return markerOf(error)?.incidentId ?? null
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Whether a response body is a production incident body — the bare incident UUID an
|
|
76
|
-
* `@owlmeans/server-api` boundary sends instead of the marshaled error. A development body
|
|
77
|
-
* carries the `ResilientError` separator and is never one.
|
|
78
|
-
*/
|
|
79
|
-
export const isIncidentBody = (data: unknown): data is string =>
|
|
80
|
-
typeof data === 'string' && !data.includes(ResilientError.separator) && UUID.test(data.trim())
|
|
81
|
-
|
|
82
|
-
export type { ResponseStatusCarrier } from './types.js'
|
|
10
|
+
/** @deprecated compat:factory-refactor — use `apiStatusHelper.httpStatusOf(…)` */
|
|
11
|
+
export const httpStatusOf = (error: unknown): number | null => apiStatusHelper.httpStatusOf(error)
|
package/src/status/types.ts
CHANGED
|
@@ -9,3 +9,28 @@ export interface ClientMarker {
|
|
|
9
9
|
status?: number
|
|
10
10
|
incidentId?: string
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/** Reads the HTTP status, the incident id and the denial kind a failed call carries. */
|
|
14
|
+
export interface ApiStatusHelper {
|
|
15
|
+
/** True only for a server-labelled auth/IAM refusal, never an unrelated 403. */
|
|
16
|
+
isAccessDenied: (error: unknown) => boolean
|
|
17
|
+
/**
|
|
18
|
+
* The HTTP status an error answers to, or `null` when nothing states one.
|
|
19
|
+
*
|
|
20
|
+
* Read in order: the status the API client stamped on the error it rebuilt from a response
|
|
21
|
+
* (`responseStatus`); the status an `ApiClientError` parsed from its marker; the `httpStatus` its
|
|
22
|
+
* class declares — a 4xx always, a 5xx only with `allowServerErrorStatus`, exactly what an
|
|
23
|
+
* `@owlmeans/server-api` boundary answers with; finally an `api:client:*` marker in the message or
|
|
24
|
+
* the type of anything else. This is what lets a browser recognise a refusal from its status
|
|
25
|
+
* alone when a production body carries nothing but an incident id.
|
|
26
|
+
*/
|
|
27
|
+
httpStatusOf: (error: unknown) => number | null
|
|
28
|
+
/** The server's incident id carried by an error — its own field, else its marker — or `null`. */
|
|
29
|
+
incidentIdOf: (error: unknown) => string | null
|
|
30
|
+
/**
|
|
31
|
+
* Whether a response body is a production incident body — the bare incident UUID an
|
|
32
|
+
* `@owlmeans/server-api` boundary sends instead of the marshaled error. A development body
|
|
33
|
+
* carries the `ResilientError` separator and is never one.
|
|
34
|
+
*/
|
|
35
|
+
isIncidentBody: (data: unknown) => data is string
|
|
36
|
+
}
|