@usearazzo/runner 0.0.0-stage → 1.0.1-alpha.9
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/CHANGELOG.md +25 -0
- package/LICENSE +202 -0
- package/NOTICE +20 -0
- package/README.md +777 -2
- package/dist/arazzo-runner.browser.js +111247 -0
- package/dist/arazzo-runner.browser.min.js +1 -0
- package/package.json +90 -4
- package/src/action/ActionResolver.cjs +160 -0
- package/src/action/ActionResolver.mjs +150 -0
- package/src/assembler/OpenAPI2DocumentAssembler.cjs +111 -0
- package/src/assembler/OpenAPI2DocumentAssembler.mjs +104 -0
- package/src/assembler/OpenAPI30DocumentAssembler.cjs +110 -0
- package/src/assembler/OpenAPI30DocumentAssembler.mjs +103 -0
- package/src/assembler/OpenAPI31DocumentAssembler.cjs +111 -0
- package/src/assembler/OpenAPI31DocumentAssembler.mjs +104 -0
- package/src/assembler/OpenAPIDocumentAssembler.cjs +58 -0
- package/src/assembler/OpenAPIDocumentAssembler.mjs +50 -0
- package/src/client/HTTPClient.cjs +5 -0
- package/src/client/HTTPClient.mjs +1 -0
- package/src/client/HTTPClientFetch.cjs +16 -0
- package/src/client/HTTPClientFetch.mjs +10 -0
- package/src/client/OpenAPIOperationRequest.cjs +5 -0
- package/src/client/OpenAPIOperationRequest.mjs +1 -0
- package/src/client/OpenAPIOperationResponse.cjs +42 -0
- package/src/client/OpenAPIOperationResponse.mjs +36 -0
- package/src/client/OpenAPIOperationResponseNormalizer.cjs +48 -0
- package/src/client/OpenAPIOperationResponseNormalizer.mjs +40 -0
- package/src/client/ParameterDelivery.cjs +119 -0
- package/src/client/ParameterDelivery.mjs +113 -0
- package/src/constants.cjs +9 -0
- package/src/constants.mjs +3 -0
- package/src/criterion/CriterionEvaluator.cjs +205 -0
- package/src/criterion/CriterionEvaluator.mjs +196 -0
- package/src/criterion/JSONPathCriterionEvaluator.cjs +47 -0
- package/src/criterion/JSONPathCriterionEvaluator.mjs +40 -0
- package/src/criterion/RegexCriterionEvaluator.cjs +67 -0
- package/src/criterion/RegexCriterionEvaluator.mjs +60 -0
- package/src/criterion/SimpleCriterionEvaluator.cjs +50 -0
- package/src/criterion/SimpleCriterionEvaluator.mjs +43 -0
- package/src/criterion/XPathCriterionEvaluator.cjs +117 -0
- package/src/criterion/XPathCriterionEvaluator.mjs +109 -0
- package/src/document/APIDocument.cjs +35 -0
- package/src/document/APIDocument.mjs +30 -0
- package/src/document/ArazzoDocument.cjs +81 -0
- package/src/document/ArazzoDocument.mjs +74 -0
- package/src/document/ArazzoSourceDescriptionIndex.cjs +26 -0
- package/src/document/ArazzoSourceDescriptionIndex.mjs +20 -0
- package/src/document/ArazzoStepIndex.cjs +25 -0
- package/src/document/ArazzoStepIndex.mjs +19 -0
- package/src/document/ArazzoWorkflowIndex.cjs +17 -0
- package/src/document/ArazzoWorkflowIndex.mjs +11 -0
- package/src/document/OpenAPIDocument.cjs +28 -0
- package/src/document/OpenAPIDocument.mjs +21 -0
- package/src/document/OpenAPIOperationIndex.cjs +24 -0
- package/src/document/OpenAPIOperationIndex.mjs +18 -0
- package/src/document/ParameterIdentity.cjs +93 -0
- package/src/document/ParameterIdentity.mjs +88 -0
- package/src/document/openapi-types.cjs +5 -0
- package/src/document/openapi-types.mjs +1 -0
- package/src/errors/ArazzoRunnerError.cjs +10 -0
- package/src/errors/ArazzoRunnerError.mjs +5 -0
- package/src/errors/AssemblerError.cjs +11 -0
- package/src/errors/AssemblerError.mjs +4 -0
- package/src/errors/ClientError.cjs +11 -0
- package/src/errors/ClientError.mjs +4 -0
- package/src/errors/CriterionError.cjs +11 -0
- package/src/errors/CriterionError.mjs +4 -0
- package/src/errors/ExecutionError.cjs +11 -0
- package/src/errors/ExecutionError.mjs +4 -0
- package/src/errors/ExtractionError.cjs +11 -0
- package/src/errors/ExtractionError.mjs +4 -0
- package/src/errors/InvalidEntryDocumentError.cjs +11 -0
- package/src/errors/InvalidEntryDocumentError.mjs +4 -0
- package/src/errors/NormalizationError.cjs +11 -0
- package/src/errors/NormalizationError.mjs +4 -0
- package/src/errors/ResolverError.cjs +11 -0
- package/src/errors/ResolverError.mjs +4 -0
- package/src/errors/RuntimeExpressionError.cjs +11 -0
- package/src/errors/RuntimeExpressionError.mjs +4 -0
- package/src/errors/UnmatchedProviderError.cjs +11 -0
- package/src/errors/UnmatchedProviderError.mjs +4 -0
- package/src/executor/ArazzoWorkflowLocatorNormalizer.cjs +147 -0
- package/src/executor/ArazzoWorkflowLocatorNormalizer.mjs +137 -0
- package/src/executor/OpenAPIOperationExecutor.cjs +457 -0
- package/src/executor/OpenAPIOperationExecutor.mjs +446 -0
- package/src/executor/OpenAPIOperationLocatorNormalizer.cjs +197 -0
- package/src/executor/OpenAPIOperationLocatorNormalizer.mjs +189 -0
- package/src/executor/SourceDescriptionsLocatorNormalizer.cjs +44 -0
- package/src/executor/SourceDescriptionsLocatorNormalizer.mjs +38 -0
- package/src/executor/StepExecutor.cjs +333 -0
- package/src/executor/StepExecutor.mjs +326 -0
- package/src/executor/StepRetryRunner.cjs +226 -0
- package/src/executor/StepRetryRunner.mjs +215 -0
- package/src/executor/StepTransitionInterpreter.cjs +159 -0
- package/src/executor/StepTransitionInterpreter.mjs +151 -0
- package/src/executor/WorkflowCallStack.cjs +116 -0
- package/src/executor/WorkflowCallStack.mjs +106 -0
- package/src/executor/WorkflowExecutor.cjs +1091 -0
- package/src/executor/WorkflowExecutor.mjs +1077 -0
- package/src/executor/abort.cjs +70 -0
- package/src/executor/abort.mjs +60 -0
- package/src/expression/RuntimeExpressionContext.cjs +5 -0
- package/src/expression/RuntimeExpressionContext.mjs +1 -0
- package/src/expression/RuntimeExpressionEvaluator.cjs +337 -0
- package/src/expression/RuntimeExpressionEvaluator.mjs +328 -0
- package/src/extractor/ArazzoStepExtractor.cjs +48 -0
- package/src/extractor/ArazzoStepExtractor.mjs +41 -0
- package/src/extractor/ArazzoWorkflowExtractor.cjs +50 -0
- package/src/extractor/ArazzoWorkflowExtractor.mjs +43 -0
- package/src/extractor/OpenAPIOperationExtractor.cjs +84 -0
- package/src/extractor/OpenAPIOperationExtractor.mjs +77 -0
- package/src/index.cjs +419 -0
- package/src/index.mjs +73 -0
- package/src/normalizer/ArazzoStepNormalizer.cjs +23 -0
- package/src/normalizer/ArazzoStepNormalizer.mjs +17 -0
- package/src/normalizer/ArazzoWorkflowNormalizer.cjs +214 -0
- package/src/normalizer/ArazzoWorkflowNormalizer.mjs +206 -0
- package/src/normalizer/OpenAPI2OperationNormalizer.cjs +128 -0
- package/src/normalizer/OpenAPI2OperationNormalizer.mjs +120 -0
- package/src/normalizer/OpenAPI30OperationNormalizer.cjs +129 -0
- package/src/normalizer/OpenAPI30OperationNormalizer.mjs +121 -0
- package/src/normalizer/OpenAPI31OperationNormalizer.cjs +131 -0
- package/src/normalizer/OpenAPI31OperationNormalizer.mjs +123 -0
- package/src/normalizer/OpenAPIOperationNormalizer.cjs +58 -0
- package/src/normalizer/OpenAPIOperationNormalizer.mjs +50 -0
- package/src/registry/DocumentRegistry.cjs +160 -0
- package/src/registry/DocumentRegistry.mjs +151 -0
- package/src/registry/providers/ArazzoDocumentRegistryProvider.cjs +154 -0
- package/src/registry/providers/ArazzoDocumentRegistryProvider.mjs +146 -0
- package/src/registry/providers/DocumentRegistryProvider.cjs +31 -0
- package/src/registry/providers/DocumentRegistryProvider.mjs +25 -0
- package/src/registry/providers/OpenAPIDocumentRegistryProvider.cjs +122 -0
- package/src/registry/providers/OpenAPIDocumentRegistryProvider.mjs +114 -0
- package/src/resolver/ArazzoValueResolver.cjs +42 -0
- package/src/resolver/ArazzoValueResolver.mjs +37 -0
- package/src/resolver/OpenAPIOperationParameterResolver.cjs +117 -0
- package/src/resolver/OpenAPIOperationParameterResolver.mjs +110 -0
- package/src/resolver/OutputResolver.cjs +71 -0
- package/src/resolver/OutputResolver.mjs +64 -0
- package/src/resolver/RequestBodyResolver.cjs +227 -0
- package/src/resolver/RequestBodyResolver.mjs +220 -0
- package/src/resolver/ResolverScope.cjs +5 -0
- package/src/resolver/ResolverScope.mjs +1 -0
- package/src/resolver/StepParameterResolver.cjs +22 -0
- package/src/resolver/StepParameterResolver.mjs +15 -0
- package/src/resolver/WorkflowParameterResolver.cjs +73 -0
- package/src/resolver/WorkflowParameterResolver.mjs +66 -0
- package/src/state/WorkflowExecutionState.cjs +99 -0
- package/src/state/WorkflowExecutionState.mjs +93 -0
- package/src/vendor/swagger-client.cjs +1 -0
- package/src/vendor/swagger-client.mjs +1 -0
- package/types/runner.d.ts +2341 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _apidomCore = require("@speclynx/apidom-core");
|
|
9
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
10
|
+
var _empty = require("@speclynx/apidom-reference/configuration/empty");
|
|
11
|
+
var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-2");
|
|
12
|
+
var _ramdaAdjunct = require("ramda-adjunct");
|
|
13
|
+
var _swaggerClient = require("../vendor/swagger-client.cjs");
|
|
14
|
+
var _ParameterDelivery = _interopRequireDefault(require("../client/ParameterDelivery.cjs"));
|
|
15
|
+
var _HTTPClientFetch = _interopRequireDefault(require("../client/HTTPClientFetch.cjs"));
|
|
16
|
+
var _OpenAPIOperationResponseNormalizer = _interopRequireDefault(require("../client/OpenAPIOperationResponseNormalizer.cjs"));
|
|
17
|
+
var _OpenAPIOperationExtractor = _interopRequireDefault(require("../extractor/OpenAPIOperationExtractor.cjs"));
|
|
18
|
+
var _OpenAPIOperationNormalizer = _interopRequireDefault(require("../normalizer/OpenAPIOperationNormalizer.cjs"));
|
|
19
|
+
var _OpenAPIDocumentAssembler = _interopRequireDefault(require("../assembler/OpenAPIDocumentAssembler.cjs"));
|
|
20
|
+
var _ClientError = _interopRequireDefault(require("../errors/ClientError.cjs"));
|
|
21
|
+
// @ts-expect-error vendored swagger-client bundle has no type declarations
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Decorates the built request before it is handed to the {@link HTTPClient}.
|
|
25
|
+
*
|
|
26
|
+
* Receives the finalized {@link OpenAPIOperationRequest} and may mutate it in
|
|
27
|
+
* place or return a replacement; returning nothing keeps the received request.
|
|
28
|
+
* May be synchronous or asynchronous.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for executing an OpenAPI operation.
|
|
34
|
+
*
|
|
35
|
+
* All fields are forwarded to the vendored swagger-client `buildRequest`
|
|
36
|
+
* (which owns parameter serialization, security application, and server URL
|
|
37
|
+
* resolution), except `requestInterceptor`, which the executor runs between
|
|
38
|
+
* building the request and sending it.
|
|
39
|
+
*
|
|
40
|
+
* Typed here are the options the runner itself drives and the ones callers
|
|
41
|
+
* routinely reach for. The rest of `buildRequest`'s surface (`securities`,
|
|
42
|
+
* `scheme`, `parameterBuilders`, `attachContentTypeForEmptyPayload`, …) is
|
|
43
|
+
* deliberately left untyped and passes through the bag's open tail unchanged —
|
|
44
|
+
* see {@link OpenAPIOperationExecuteOptionsBag}. Authentication in particular
|
|
45
|
+
* is not modeled: Arazzo is auth-agnostic, so credentials are applied with a
|
|
46
|
+
* {@link RequestInterceptor}.
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The execute options forwarded to {@link OpenAPIOperationExecutor.execute}.
|
|
52
|
+
*
|
|
53
|
+
* The {@link OpenAPIOperationExecuteOptions} fields are surfaced for
|
|
54
|
+
* type-checked callers, while the open `Record` tail keeps the bag opaque so
|
|
55
|
+
* the rest of `buildRequest`'s surface (`scheme`, `parameterBuilders`,
|
|
56
|
+
* `attachContentTypeForEmptyPayload`, …) passes through untouched.
|
|
57
|
+
*
|
|
58
|
+
* Nothing routine belongs in the tail. Credentials go through a
|
|
59
|
+
* {@link RequestInterceptor} and `server` covers every case that once needed a
|
|
60
|
+
* `contextUrl`, so reaching for either of those here means an edge case: tests
|
|
61
|
+
* standing in for a document fetched over HTTP, or credentials applied from the
|
|
62
|
+
* document's own security schemes.
|
|
63
|
+
*
|
|
64
|
+
* Two kinds of entry are inert rather than honored, since the executor owns
|
|
65
|
+
* what they used to control. The operation target always comes from the
|
|
66
|
+
* locator, so `operationId`/`operationPath` have no effect; and the options
|
|
67
|
+
* only swagger-client's `execute()` ever read — `userFetch` and
|
|
68
|
+
* `responseInterceptor`, which the runner replaces with `httpClient` and
|
|
69
|
+
* `responseNormalizer` — reach `buildRequest` but nothing acts on them.
|
|
70
|
+
* @public
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Options for the OpenAPIOperationExecutor.
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Executes a single OpenAPI operation named by a canonical
|
|
80
|
+
* {@link OpenAPIOperationLocator}.
|
|
81
|
+
*
|
|
82
|
+
* The pipeline extracts the operation from its owning document, normalizes it,
|
|
83
|
+
* assembles a minimal standalone OpenAPI document containing just that
|
|
84
|
+
* operation, builds the HTTP request with the vendored swagger-client
|
|
85
|
+
* `buildRequest` (parameter serialization, securities, server resolution),
|
|
86
|
+
* sends it through the pluggable {@link HTTPClient}, and normalizes the raw
|
|
87
|
+
* `Response` into the runner's response model. A non-2xx status is a valid
|
|
88
|
+
* outcome (judged by a step's `successCriteria`) and is returned, never
|
|
89
|
+
* thrown; a transport failure is wrapped as a {@link ClientError}.
|
|
90
|
+
*
|
|
91
|
+
* The executor is Arazzo-agnostic: it neither locates the operation (that is
|
|
92
|
+
* the operation locator normalizer's concern) nor resolves parameters or
|
|
93
|
+
* request bodies from runtime expressions (the step executor's concern). It
|
|
94
|
+
* receives an already-resolved locator and an opaque bag of execute options,
|
|
95
|
+
* and returns the normalized operation response carrying the request that was
|
|
96
|
+
* actually sent, so `$url` / `$method` / `$request.*` can be evaluated after
|
|
97
|
+
* execution.
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
class OpenAPIOperationExecutor {
|
|
101
|
+
#httpClient;
|
|
102
|
+
#requestInterceptor;
|
|
103
|
+
#responseNormalizer;
|
|
104
|
+
#operationExtractor;
|
|
105
|
+
#operationNormalizer;
|
|
106
|
+
#documentAssembler;
|
|
107
|
+
constructor(options = {}) {
|
|
108
|
+
this.#httpClient = options.httpClient ?? _HTTPClientFetch.default;
|
|
109
|
+
this.#requestInterceptor = options.requestInterceptor;
|
|
110
|
+
this.#responseNormalizer = options.responseNormalizer ?? new _OpenAPIOperationResponseNormalizer.default();
|
|
111
|
+
this.#operationExtractor = options.operationExtractor ?? new _OpenAPIOperationExtractor.default();
|
|
112
|
+
this.#operationNormalizer = options.operationNormalizer ?? new _OpenAPIOperationNormalizer.default();
|
|
113
|
+
this.#documentAssembler = options.documentAssembler ?? new _OpenAPIDocumentAssembler.default();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Executes the located operation, returning the normalized operation
|
|
118
|
+
* response.
|
|
119
|
+
*
|
|
120
|
+
* `executeOptions` is an opaque bag of `buildRequest` options (e.g. resolved
|
|
121
|
+
* `parameters`, a `requestBody` and its `requestContentType`, or a `server`).
|
|
122
|
+
* The operation target always takes precedence over it: the
|
|
123
|
+
* locator's JSON Pointer selects the operation in the assembled document, so
|
|
124
|
+
* `operationId`/`operationPath` entries in the bag cannot hijack the
|
|
125
|
+
* operation.
|
|
126
|
+
*/
|
|
127
|
+
async execute(locator, executeOptions = {}) {
|
|
128
|
+
// extraction validates the locator first: a pointer naming anything but an
|
|
129
|
+
// operation would otherwise crash inside normalization
|
|
130
|
+
const operation = this.#operationExtractor.extractByPointer(locator.document, locator.jsonPointer);
|
|
131
|
+
// the legacy id (`{method}-{path}`) matches regardless of an explicit
|
|
132
|
+
// `operationId` in the assembled document
|
|
133
|
+
const operationId = (0, _swaggerClient.idFromPathMethodLegacy)((0, _apidomCore.toValue)(operation.meta.get('path')), (0, _apidomCore.toValue)(operation.meta.get('http-method')));
|
|
134
|
+
const normalizedOperation = await this.#operationNormalizer.normalize(operation, locator.document);
|
|
135
|
+
const assembled = this.#documentAssembler.assemble(normalizedOperation, locator.document);
|
|
136
|
+
|
|
137
|
+
// the interceptor is consumed here, and the target always comes from the
|
|
138
|
+
// locator, so both operation keys are dropped rather than rest
|
|
139
|
+
const {
|
|
140
|
+
requestInterceptor,
|
|
141
|
+
operationId: _,
|
|
142
|
+
operationPath: __,
|
|
143
|
+
...rest
|
|
144
|
+
} = executeOptions;
|
|
145
|
+
|
|
146
|
+
// each step derives the next options rather than editing in place
|
|
147
|
+
const withServer = this.#applyServerOptions(rest, normalizedOperation, locator.document);
|
|
148
|
+
const withBody = this.#adaptRequestBody(withServer, normalizedOperation, locator.document, locator.jsonPointer);
|
|
149
|
+
const buildOptions = this.#adaptRequestContentType(withBody, normalizedOperation, locator.document, locator.jsonPointer);
|
|
150
|
+
let request;
|
|
151
|
+
try {
|
|
152
|
+
const spec = assembled.toJSON();
|
|
153
|
+
request = (0, _swaggerClient.buildRequest)({
|
|
154
|
+
...buildOptions,
|
|
155
|
+
spec,
|
|
156
|
+
operationId
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw new _ClientError.default('Failed to build request for OpenAPI operation', {
|
|
160
|
+
operationPath: locator.jsonPointer,
|
|
161
|
+
cause: error
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// a differing value means #adaptRequestContentType swapped the authored
|
|
166
|
+
// media type for the declared one so the body would attach; the header
|
|
167
|
+
// still goes out as authored, parameters and all
|
|
168
|
+
const authoredContentType = executeOptions.requestContentType;
|
|
169
|
+
if (typeof authoredContentType === 'string' && buildOptions.requestContentType !== authoredContentType) {
|
|
170
|
+
request.headers['Content-Type'] = authoredContentType;
|
|
171
|
+
}
|
|
172
|
+
request = await this.#finalizeRequest(request, requestInterceptor);
|
|
173
|
+
|
|
174
|
+
// capture the request as sent, so `$url` / `$method` / `$request.*` can be
|
|
175
|
+
// evaluated after execution; headers are copied so a transport-side
|
|
176
|
+
// mutation cannot rewrite the record
|
|
177
|
+
const requestInfo = {
|
|
178
|
+
url: request.url,
|
|
179
|
+
method: request.method,
|
|
180
|
+
headers: {
|
|
181
|
+
...request.headers
|
|
182
|
+
},
|
|
183
|
+
body: request.body
|
|
184
|
+
};
|
|
185
|
+
let rawResponse;
|
|
186
|
+
try {
|
|
187
|
+
rawResponse = await this.#httpClient(request);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new _ClientError.default('OpenAPI operation request failed', {
|
|
190
|
+
operationPath: locator.jsonPointer,
|
|
191
|
+
cause: error
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return this.#responseNormalizer.normalize(rawResponse, requestInfo);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Derives where the request is sent, returning the options to build with.
|
|
199
|
+
*
|
|
200
|
+
* swagger-client already selects a declared server, substitutes its variables
|
|
201
|
+
* and resolves relative URLs — the one thing it gets wrong is what to do with
|
|
202
|
+
* a `server` matching nothing it declared, which it silently swaps for the
|
|
203
|
+
* first declared server. Here that value becomes an override instead: the URL
|
|
204
|
+
* you name is the URL that is called, whatever the document declares. Passing
|
|
205
|
+
* it as `baseURL` bypasses swagger-client's own server resolution, which also
|
|
206
|
+
* makes `server` work on Swagger 2.0, where it is otherwise ignored.
|
|
207
|
+
*
|
|
208
|
+
* A relative server resolves against the URL the document was loaded from,
|
|
209
|
+
* which is what `contextUrl` means, so it is derived rather than asked for.
|
|
210
|
+
* Only an HTTP(S) document URL qualifies. Swagger 2.0 falls back to the
|
|
211
|
+
* context URL's protocol when the document declares no `schemes`, so a `file:`
|
|
212
|
+
* URL would yield `file://host/basePath` instead of `http://…`. The registry
|
|
213
|
+
* normalizes `file:` URIs to bare filesystem paths, which carry no protocol
|
|
214
|
+
* and are harmless, so this is a guard on the invariant rather than on a
|
|
215
|
+
* reachable state today. A caller-supplied `contextUrl` still wins, for
|
|
216
|
+
* resolving against somewhere the document did not come from.
|
|
217
|
+
*/
|
|
218
|
+
#applyServerOptions(buildOptions, operation, document) {
|
|
219
|
+
const resolved = buildOptions.contextUrl === undefined && _empty.url.isHttpUrl(document.uri) ? {
|
|
220
|
+
...buildOptions,
|
|
221
|
+
contextUrl: document.uri
|
|
222
|
+
} : buildOptions;
|
|
223
|
+
const {
|
|
224
|
+
server,
|
|
225
|
+
...withoutServer
|
|
226
|
+
} = resolved;
|
|
227
|
+
if (typeof server !== 'string') return resolved;
|
|
228
|
+
|
|
229
|
+
// the operation normalizer has already inherited servers down from the
|
|
230
|
+
// document root, including the spec's default `[{ url: '/' }]`. spread into
|
|
231
|
+
// a real array rather than using ArrayElement's own find(), which returns an
|
|
232
|
+
// empty ArrayElement on a miss — truthy, so every server would look declared.
|
|
233
|
+
// the members are Server elements, but isObjectElement keeps this one code
|
|
234
|
+
// path valid for every version: isServerElement is per-namespace, and the
|
|
235
|
+
// 3.1 one rejects 3.0 servers
|
|
236
|
+
const servers = operation.get('servers');
|
|
237
|
+
const declared = (0, _apidomDatamodel.isArrayElement)(servers) ? [...servers] : [];
|
|
238
|
+
const isDeclared = declared.some(candidate => (0, _apidomDatamodel.isObjectElement)(candidate) && (0, _apidomCore.toValue)(candidate.get('url')) === server);
|
|
239
|
+
if (isDeclared) return resolved;
|
|
240
|
+
const baseURL = _empty.url.hasProtocol(server) ? server : _empty.url.resolve(resolved.contextUrl ?? '', server);
|
|
241
|
+
|
|
242
|
+
// buildRequest concatenates the base and the path verbatim, so a trailing
|
|
243
|
+
// slash would double up. swagger-client trims one in its own resolution too
|
|
244
|
+
return {
|
|
245
|
+
...withoutServer,
|
|
246
|
+
baseURL: baseURL.replace(/\/$/, '')
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Reconciles the requested body media type with the ones the operation
|
|
252
|
+
* declares, returning the options to build with. A rewritten
|
|
253
|
+
* `requestContentType` is what tells `execute` to restore the authored value
|
|
254
|
+
* onto the header afterwards.
|
|
255
|
+
*
|
|
256
|
+
* swagger-client attaches the body only when `requestContentType` is an exact
|
|
257
|
+
* key of `requestBody.content`, and otherwise drops the body *and* the header
|
|
258
|
+
* without a word. Media types are richer than that: they carry optional
|
|
259
|
+
* parameters and their names are case-insensitive, so the Arazzo
|
|
260
|
+
* `application/json; charset=utf-8` and `Application/JSON` both name a
|
|
261
|
+
* declared `application/json`. Both are matched here, the request is built
|
|
262
|
+
* under the declared spelling, and the authored value is restored onto the
|
|
263
|
+
* header afterwards. A type naming nothing declared is an authoring error and
|
|
264
|
+
* is raised rather than turned into an empty request.
|
|
265
|
+
*
|
|
266
|
+
* Only OpenAPI 3.x declares media types this way. A Swagger 2.0 operation has
|
|
267
|
+
* no `requestBody` to check against, so its content type passes through for
|
|
268
|
+
* `consumes` to govern.
|
|
269
|
+
*/
|
|
270
|
+
#adaptRequestContentType(buildOptions, operation, document, jsonPointer) {
|
|
271
|
+
const requested = buildOptions.requestContentType;
|
|
272
|
+
if (typeof requested !== 'string' || buildOptions.requestBody === undefined) {
|
|
273
|
+
return buildOptions;
|
|
274
|
+
}
|
|
275
|
+
if ((0, _apidomNsOpenapi.isSwaggerElement)(document.parseResult.api)) return buildOptions;
|
|
276
|
+
const requestBody = operation.get('requestBody');
|
|
277
|
+
const content = (0, _apidomDatamodel.isObjectElement)(requestBody) ? requestBody.get('content') : undefined;
|
|
278
|
+
if (!(0, _apidomDatamodel.isObjectElement)(content)) {
|
|
279
|
+
throw new _ClientError.default(`Request body media type "${requested}" cannot be sent: the operation declares ` + 'no request body content. Omit the content type to send the payload as-is', {
|
|
280
|
+
operationPath: jsonPointer
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const declared = content.keys();
|
|
284
|
+
if (declared.includes(requested)) return buildOptions;
|
|
285
|
+
|
|
286
|
+
// media type names are case-insensitive and their parameters are not part of
|
|
287
|
+
// the name, so both are normalized away before matching
|
|
288
|
+
const baseType = requested.split(';')[0].trim().toLowerCase();
|
|
289
|
+
const [type] = baseType.split('/');
|
|
290
|
+
const covers = (mediaType, by) => {
|
|
291
|
+
const candidate = mediaType.toLowerCase();
|
|
292
|
+
if (by === 'exact') return candidate === baseType;
|
|
293
|
+
if (by === 'subtype') return candidate === `${type}/*`;
|
|
294
|
+
return candidate === '*/*';
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// a form media type decides how the body is encoded, and buildRequest applies
|
|
298
|
+
// that encoding only for an exactly declared type. matching one against a
|
|
299
|
+
// range would put a JSON body behind a form Content-Type
|
|
300
|
+
const isFormEncoded = baseType === 'application/x-www-form-urlencoded' || baseType === 'multipart/form-data';
|
|
301
|
+
|
|
302
|
+
// otherwise a declared range covers a concrete type, so `*/*` (what several
|
|
303
|
+
// generators emit by default) accepts anything; the most specific wins
|
|
304
|
+
const exact = declared.find(mediaType => covers(mediaType, 'exact'));
|
|
305
|
+
const match = exact ?? (isFormEncoded ? undefined : declared.find(mediaType => covers(mediaType, 'subtype')) ?? declared.find(mediaType => covers(mediaType, 'any')));
|
|
306
|
+
if (match === undefined) {
|
|
307
|
+
throw new _ClientError.default(isFormEncoded ? `Request body media type "${requested}" must be declared by the operation exactly, ` + `since it decides how the body is encoded (declared: [${declared.join(', ')}])` : `Request body media type "${requested}" is not declared by the operation ` + `(declared: [${declared.join(', ')}])`, {
|
|
308
|
+
operationPath: jsonPointer
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
...buildOptions,
|
|
313
|
+
requestContentType: match
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Routes a request body through the convention the target OpenAPI version
|
|
319
|
+
* uses, leaving OpenAPI 3.x calls untouched.
|
|
320
|
+
*
|
|
321
|
+
* Swagger 2.0 has no request body concept — the payload is an ordinary
|
|
322
|
+
* parameter declared `in: body`, or a set of `in: formData` ones — so it must
|
|
323
|
+
* travel through `parameters` under the names the operation declares. Arazzo
|
|
324
|
+
* cannot express that itself (its parameter `in` allows only `path`, `query`,
|
|
325
|
+
* `querystring`, `header`, and `cookie`), which is why the translation lives
|
|
326
|
+
* here, where the assembled document has those parameters dereferenced and
|
|
327
|
+
* inherited.
|
|
328
|
+
*
|
|
329
|
+
* The payload travels under {@link ParameterDelivery} keys
|
|
330
|
+
* (`body.{name}`, `formData.{name}`), never bare names: a bare key would
|
|
331
|
+
* capture a same-named parameter in another location — 2.0 legally declares
|
|
332
|
+
* e.g. `petId` in `path` and in `formData` at once — per the lookup order
|
|
333
|
+
* documented on {@link ParameterDelivery}.
|
|
334
|
+
*
|
|
335
|
+
* A *bare* `parameters` entry bearing a payload target's name is rejected
|
|
336
|
+
* with {@link ClientError} rather than resolved either way: before the
|
|
337
|
+
* qualified keys the payload silently overwrote it, after them the bare key
|
|
338
|
+
* would silently outrank the payload at the client's bare-first lookup —
|
|
339
|
+
* both wrong for somebody, so the ambiguity is reported instead. Such an
|
|
340
|
+
* entry can come from a direct caller mixing bare parameters with a request
|
|
341
|
+
* body, or from the runner's own pipeline: the parameter resolver keeps a
|
|
342
|
+
* `querystring` parameter under its bare name.
|
|
343
|
+
*/
|
|
344
|
+
#adaptRequestBody(buildOptions, operation, document, jsonPointer) {
|
|
345
|
+
if (!(0, _apidomNsOpenapi.isSwaggerElement)(document.parseResult.api) || buildOptions.requestBody === undefined) {
|
|
346
|
+
return buildOptions;
|
|
347
|
+
}
|
|
348
|
+
const {
|
|
349
|
+
requestBody,
|
|
350
|
+
parameters: given,
|
|
351
|
+
...rest
|
|
352
|
+
} = buildOptions;
|
|
353
|
+
const callerParameters = given;
|
|
354
|
+
// an operation declaring none leaves `parameters` absent rather than empty,
|
|
355
|
+
// and that case still has to reach the throw below
|
|
356
|
+
const declared = operation.parameters;
|
|
357
|
+
const parameters = (0, _apidomDatamodel.isArrayElement)(declared) ? [...declared.filter(_apidomNsOpenapi.isParameterElement)] : [];
|
|
358
|
+
|
|
359
|
+
// the body parameter's name is arbitrary, so the payload wins over a
|
|
360
|
+
// same-keyed entry the caller supplied
|
|
361
|
+
const body = parameters.find(parameter => (0, _apidomCore.toValue)(parameter.in) === 'body');
|
|
362
|
+
if (body !== undefined) {
|
|
363
|
+
const name = (0, _apidomCore.toValue)(body.name);
|
|
364
|
+
this.#rejectBarePayloadConflicts(callerParameters, [name], jsonPointer);
|
|
365
|
+
return {
|
|
366
|
+
...rest,
|
|
367
|
+
parameters: {
|
|
368
|
+
...callerParameters,
|
|
369
|
+
[_ParameterDelivery.default.keyFor('body', name)]: requestBody
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// formData models each field as its own parameter, so the payload's keys
|
|
375
|
+
// are the parameter names; buildRequest reports any that do not match
|
|
376
|
+
const hasForm = parameters.some(parameter => (0, _apidomCore.toValue)(parameter.in) === 'formData');
|
|
377
|
+
if (hasForm) {
|
|
378
|
+
if (!(0, _ramdaAdjunct.isPlainObject)(requestBody)) {
|
|
379
|
+
throw new _ClientError.default('Request body cannot be sent: the OpenAPI 2.0 operation carries it in "formData" ' + 'parameters, so the payload must be an object keyed by their names', {
|
|
380
|
+
operationPath: jsonPointer
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
const payload = requestBody;
|
|
384
|
+
this.#rejectBarePayloadConflicts(callerParameters, Object.keys(payload), jsonPointer);
|
|
385
|
+
const merged = {
|
|
386
|
+
...callerParameters
|
|
387
|
+
};
|
|
388
|
+
for (const [field, value] of Object.entries(payload)) {
|
|
389
|
+
merged[_ParameterDelivery.default.keyFor('formData', field)] = value;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
...rest,
|
|
393
|
+
parameters: merged
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
throw new _ClientError.default('Request body cannot be sent: the OpenAPI 2.0 operation declares no "body" or ' + '"formData" parameter to carry it', {
|
|
397
|
+
operationPath: jsonPointer
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Rejects bare `parameters` entries bearing the names the payload is about
|
|
403
|
+
* to be delivered under — the client consults bare names before qualified
|
|
404
|
+
* keys, so such an entry would silently displace the request body (see
|
|
405
|
+
* {@link OpenAPIOperationExecutor.#adaptRequestBody}).
|
|
406
|
+
*/
|
|
407
|
+
#rejectBarePayloadConflicts(callerParameters, payloadNames, jsonPointer) {
|
|
408
|
+
if (callerParameters === undefined) return;
|
|
409
|
+
const conflicts = payloadNames.filter(name => Object.hasOwn(callerParameters, name));
|
|
410
|
+
if (conflicts.length === 0) return;
|
|
411
|
+
throw new _ClientError.default(`Request body cannot be sent: bare parameter ${conflicts.length === 1 ? 'entry' : 'entries'} ` + `[${conflicts.join(', ')}] would displace the payload at delivery. Supply the ` + `parameter under its "{in}.{name}" key, or drop the duplicate`, {
|
|
412
|
+
operationPath: jsonPointer
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Applies the wire-level fixups swagger-client performs between building a
|
|
418
|
+
* request and fetching it, and runs the request interceptors: the
|
|
419
|
+
* executor-level one first, the per-call one from the options bag second.
|
|
420
|
+
*
|
|
421
|
+
* Ported rather than imported: upstream keeps them inline in `execute()` and
|
|
422
|
+
* `http()`, which also fetch and throw on a non-2xx. Diff against those two
|
|
423
|
+
* when bumping swagger-client.
|
|
424
|
+
*/
|
|
425
|
+
async #finalizeRequest(request, perCallInterceptor) {
|
|
426
|
+
let finalized = request;
|
|
427
|
+
|
|
428
|
+
// JSON-encode plain-object and array bodies, exactly as swagger-client's
|
|
429
|
+
// execute() does before handing the request to its transport
|
|
430
|
+
if (finalized.body && ((0, _ramdaAdjunct.isPlainObject)(finalized.body) || Array.isArray(finalized.body))) {
|
|
431
|
+
finalized.body = JSON.stringify(finalized.body);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// an embedded newline makes a header value invalid per the Fetch standard,
|
|
435
|
+
// so fetch throws a TypeError before sending anything. squashing repairs it,
|
|
436
|
+
// and since the request is captured after this, the record shows the value
|
|
437
|
+
// that actually went out
|
|
438
|
+
Object.keys(finalized.headers).forEach(headerName => {
|
|
439
|
+
const value = finalized.headers[headerName];
|
|
440
|
+
if (typeof value === 'string') {
|
|
441
|
+
finalized.headers[headerName] = value.replace(/\n+/g, ' ');
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
finalized = (await this.#requestInterceptor?.(finalized)) ?? finalized;
|
|
445
|
+
finalized = (await perCallInterceptor?.(finalized)) ?? finalized;
|
|
446
|
+
|
|
447
|
+
// for multipart/form-data, remove the built content-type so the transport
|
|
448
|
+
// sets one carrying the correct boundary
|
|
449
|
+
const contentType = finalized.headers['content-type'] ?? finalized.headers['Content-Type'];
|
|
450
|
+
if (typeof contentType === 'string' && /multipart\/form-data/i.test(contentType)) {
|
|
451
|
+
delete finalized.headers['content-type'];
|
|
452
|
+
delete finalized.headers['Content-Type'];
|
|
453
|
+
}
|
|
454
|
+
return finalized;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
var _default = exports.default = OpenAPIOperationExecutor;
|