1 |
|
2 |
|
3 |
|
4 | import { TokenCredential, isTokenCredential } from "@azure/core-auth";
|
5 | import { ServiceClientCredentials } from "./credentials/serviceClientCredentials";
|
6 | import { DefaultHttpClient } from "./defaultHttpClient";
|
7 | import { HttpClient } from "./httpClient";
|
8 | import { HttpOperationResponse, RestResponse } from "./httpOperationResponse";
|
9 | import { HttpPipelineLogger } from "./httpPipelineLogger";
|
10 | import { OperationArguments } from "./operationArguments";
|
11 | import {
|
12 | getPathStringFromParameter,
|
13 | getPathStringFromParameterPath,
|
14 | OperationParameter,
|
15 | ParameterPath,
|
16 | } from "./operationParameter";
|
17 | import { isStreamOperation, OperationSpec } from "./operationSpec";
|
18 | import {
|
19 | deserializationPolicy,
|
20 | DeserializationContentTypes,
|
21 | } from "./policies/deserializationPolicy";
|
22 | import { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
|
23 | import { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
|
24 | import {
|
25 | userAgentPolicy,
|
26 | getDefaultUserAgentHeaderName,
|
27 | getDefaultUserAgentValue,
|
28 | } from "./policies/userAgentPolicy";
|
29 | import { DefaultRedirectOptions, RedirectOptions, redirectPolicy } from "./policies/redirectPolicy";
|
30 | import {
|
31 | RequestPolicy,
|
32 | RequestPolicyFactory,
|
33 | RequestPolicyOptions,
|
34 | RequestPolicyOptionsLike,
|
35 | } from "./policies/requestPolicy";
|
36 | import { rpRegistrationPolicy } from "./policies/rpRegistrationPolicy";
|
37 | import { signingPolicy } from "./policies/signingPolicy";
|
38 | import { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
|
39 | import { QueryCollectionFormat } from "./queryCollectionFormat";
|
40 | import { CompositeMapper, DictionaryMapper, Mapper, MapperType, Serializer } from "./serializer";
|
41 | import { URLBuilder } from "./url";
|
42 | import * as utils from "./util/utils";
|
43 | import { stringifyXML } from "./util/xml";
|
44 | import {
|
45 | RequestOptionsBase,
|
46 | RequestPrepareOptions,
|
47 | WebResourceLike,
|
48 | isWebResourceLike,
|
49 | WebResource,
|
50 | } from "./webResource";
|
51 | import { OperationResponse } from "./operationResponse";
|
52 | import { ServiceCallback } from "./util/utils";
|
53 | import { agentPolicy } from "./policies/agentPolicy";
|
54 | import { proxyPolicy, getDefaultProxySettings } from "./policies/proxyPolicy";
|
55 | import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy";
|
56 | import { Agent } from "http";
|
57 | import {
|
58 | AzureIdentityCredentialAdapter,
|
59 | azureResourceManagerEndpoints,
|
60 | } from "./credentials/azureIdentityTokenCredentialAdapter";
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | export interface ProxySettings {
|
66 | host: string;
|
67 | port: number;
|
68 | username?: string;
|
69 | password?: string;
|
70 | }
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | export interface AgentSettings {
|
76 | http: Agent;
|
77 | https: Agent;
|
78 | }
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | export interface ServiceClientOptions {
|
84 | |
85 |
|
86 |
|
87 |
|
88 |
|
89 | requestPolicyFactories?:
|
90 | | RequestPolicyFactory[]
|
91 | | ((defaultRequestPolicyFactories: RequestPolicyFactory[]) => void | RequestPolicyFactory[]);
|
92 | /**
|
93 | * The HttpClient that will be used to send HTTP requests.
|
94 | */
|
95 | httpClient?: HttpClient;
|
96 | /**
|
97 | * The HttpPipelineLogger that can be used to debug RequestPolicies within the HTTP pipeline.
|
98 | */
|
99 | httpPipelineLogger?: HttpPipelineLogger;
|
100 | /**
|
101 | * If set to true, turn off the default retry policy.
|
102 | */
|
103 | noRetryPolicy?: boolean;
|
104 | /**
|
105 | * Gets or sets the retry timeout in seconds for AutomaticRPRegistration. Default value is 30.
|
106 | */
|
107 | rpRegistrationRetryTimeout?: number;
|
108 | /**
|
109 | * Whether or not to generate a client request ID header for each HTTP request.
|
110 | */
|
111 | generateClientRequestIdHeader?: boolean;
|
112 | /**
|
113 | * Whether to include credentials in CORS requests in the browser.
|
114 | * See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials for more information.
|
115 | */
|
116 | withCredentials?: boolean;
|
117 | /**
|
118 | * If specified, a GenerateRequestIdPolicy will be added to the HTTP pipeline that will add a
|
119 | * header to all outgoing requests with this header name and a random UUID as the request ID.
|
120 | */
|
121 | clientRequestIdHeaderName?: string;
|
122 | /**
|
123 | * The content-types that will be associated with JSON or XML serialization.
|
124 | */
|
125 | deserializationContentTypes?: DeserializationContentTypes;
|
126 | /**
|
127 | * The header name to use for the telemetry header while sending the request. If this is not
|
128 | * specified, then "User-Agent" will be used when running on Node.js and "x-ms-command-name" will
|
129 | * be used when running in a browser.
|
130 | */
|
131 | userAgentHeaderName?: string | ((defaultUserAgentHeaderName: string) => string);
|
132 | /**
|
133 | * The string to be set to the telemetry header while sending the request, or a function that
|
134 | * takes in the default user-agent string and returns the user-agent string that will be used.
|
135 | */
|
136 | userAgent?: string | ((defaultUserAgent: string) => string);
|
137 | /**
|
138 | * Proxy settings which will be used for every HTTP request (Node.js only).
|
139 | */
|
140 | proxySettings?: ProxySettings;
|
141 | /**
|
142 | * Options for how redirect responses are handled.
|
143 | */
|
144 | redirectOptions?: RedirectOptions;
|
145 | /**
|
146 | * HTTP and HTTPS agents which will be used for every HTTP request (Node.js only).
|
147 | */
|
148 | agentSettings?: AgentSettings;
|
149 | /**
|
150 | * If specified:
|
151 | * - This `baseUri` becomes the base URI that requests will be made against for this ServiceClient.
|
152 | * - If the `baseUri` matches a known resource manager endpoint and if a `TokenCredential` was passed through the constructor, this `baseUri` defines the `getToken` scope to be `${options.baseUri}/.default`. Otherwise, the scope would default to "https://management.azure.com/.default".
|
153 | *
|
154 | * If it is not specified:
|
155 | * - All OperationSpecs must contain a baseUrl property.
|
156 | * - If a `TokenCredential` was passed through the constructor, the `getToken` scope is set to be "https://management.azure.com/.default".
|
157 | */
|
158 | baseUri?: string;
|
159 | }
|
160 |
|
161 | /**
|
162 | * @class
|
163 | * Initializes a new instance of the ServiceClient.
|
164 | */
|
165 | export class ServiceClient {
|
166 | /**
|
167 | * The base URI against which requests will be made when using this ServiceClient instance.
|
168 | *
|
169 | * This can be set either by setting the `baseUri` in the `options` parameter to the ServiceClient constructor or directly after constructing the ServiceClient.
|
170 | * If set via the ServiceClient constructor when using the overload that takes the `TokenCredential`, and if it matches a known resource manager endpoint, this base URI sets the scope used to get the AAD token to `${baseUri}/.default` instead of the default "https://management.azure.com/.default"
|
171 | *
|
172 | * If it is not specified, all OperationSpecs must contain a baseUrl property.
|
173 | */
|
174 | protected baseUri?: string;
|
175 |
|
176 | /**
|
177 | * The default request content type for the service.
|
178 | * Used if no requestContentType is present on an OperationSpec.
|
179 | */
|
180 | protected requestContentType?: string;
|
181 |
|
182 | /**
|
183 | * The HTTP client that will be used to send requests.
|
184 | */
|
185 | private readonly _httpClient: HttpClient;
|
186 | private readonly _requestPolicyOptions: RequestPolicyOptionsLike;
|
187 |
|
188 | private readonly _requestPolicyFactories: RequestPolicyFactory[];
|
189 | private readonly _withCredentials: boolean;
|
190 |
|
191 | /**
|
192 | * The ServiceClient constructor
|
193 | * @constructor
|
194 | * @param {ServiceClientCredentials} [credentials] The credentials object used for authentication.
|
195 | * @param {ServiceClientOptions} [options] The service client options that govern the behavior of the client.
|
196 | */
|
197 | constructor(
|
198 | credentials?: ServiceClientCredentials | TokenCredential,
|
199 | options?: ServiceClientOptions
|
200 | ) {
|
201 | if (!options) {
|
202 | options = {};
|
203 | }
|
204 |
|
205 | if (options.baseUri) {
|
206 | this.baseUri = options.baseUri;
|
207 | }
|
208 |
|
209 | let serviceClientCredentials: ServiceClientCredentials | undefined;
|
210 | if (isTokenCredential(credentials)) {
|
211 | let scope: string | undefined = undefined;
|
212 | if (options?.baseUri && azureResourceManagerEndpoints.includes(options?.baseUri)) {
|
213 | scope = `${options.baseUri}/.default`;
|
214 | }
|
215 | serviceClientCredentials = new AzureIdentityCredentialAdapter(credentials, scope);
|
216 | } else {
|
217 | serviceClientCredentials = credentials;
|
218 | }
|
219 |
|
220 | if (serviceClientCredentials && !serviceClientCredentials.signRequest) {
|
221 | throw new Error("credentials argument needs to implement signRequest method");
|
222 | }
|
223 |
|
224 | this._withCredentials = options.withCredentials || false;
|
225 | this._httpClient = options.httpClient || new DefaultHttpClient();
|
226 | this._requestPolicyOptions = new RequestPolicyOptions(options.httpPipelineLogger);
|
227 |
|
228 | let requestPolicyFactories: RequestPolicyFactory[];
|
229 | if (Array.isArray(options.requestPolicyFactories)) {
|
230 | requestPolicyFactories = options.requestPolicyFactories;
|
231 | } else {
|
232 | requestPolicyFactories = createDefaultRequestPolicyFactories(
|
233 | serviceClientCredentials,
|
234 | options
|
235 | );
|
236 | if (options.requestPolicyFactories) {
|
237 | const newRequestPolicyFactories:
|
238 | | void
|
239 | | RequestPolicyFactory[] = options.requestPolicyFactories(requestPolicyFactories);
|
240 | if (newRequestPolicyFactories) {
|
241 | requestPolicyFactories = newRequestPolicyFactories;
|
242 | }
|
243 | }
|
244 | }
|
245 | this._requestPolicyFactories = requestPolicyFactories;
|
246 | }
|
247 |
|
248 | /**
|
249 | * Send the provided httpRequest.
|
250 | */
|
251 | sendRequest(options: RequestPrepareOptions | WebResourceLike): Promise<HttpOperationResponse> {
|
252 | if (options === null || options === undefined || typeof options !== "object") {
|
253 | throw new Error("options cannot be null or undefined and it must be of type object.");
|
254 | }
|
255 |
|
256 | let httpRequest: WebResourceLike;
|
257 | try {
|
258 | if (isWebResourceLike(options)) {
|
259 | options.validateRequestProperties();
|
260 | httpRequest = options;
|
261 | } else {
|
262 | httpRequest = new WebResource();
|
263 | httpRequest = httpRequest.prepare(options);
|
264 | }
|
265 | } catch (error) {
|
266 | return Promise.reject(error);
|
267 | }
|
268 |
|
269 | let httpPipeline: RequestPolicy = this._httpClient;
|
270 | if (this._requestPolicyFactories && this._requestPolicyFactories.length > 0) {
|
271 | for (let i = this._requestPolicyFactories.length - 1; i >= 0; --i) {
|
272 | httpPipeline = this._requestPolicyFactories[i].create(
|
273 | httpPipeline,
|
274 | this._requestPolicyOptions
|
275 | );
|
276 | }
|
277 | }
|
278 | return httpPipeline.sendRequest(httpRequest);
|
279 | }
|
280 |
|
281 | /**
|
282 | * Send an HTTP request that is populated using the provided OperationSpec.
|
283 | * @param {OperationArguments} operationArguments The arguments that the HTTP request's templated values will be populated from.
|
284 | * @param {OperationSpec} operationSpec The OperationSpec to use to populate the httpRequest.
|
285 | * @param {ServiceCallback} callback The callback to call when the response is received.
|
286 | */
|
287 | sendOperationRequest(
|
288 | operationArguments: OperationArguments,
|
289 | operationSpec: OperationSpec,
|
290 | callback?: ServiceCallback<any>
|
291 | ): Promise<RestResponse> {
|
292 | if (typeof operationArguments.options === "function") {
|
293 | callback = operationArguments.options;
|
294 | operationArguments.options = undefined;
|
295 | }
|
296 |
|
297 | const httpRequest = new WebResource();
|
298 |
|
299 | let result: Promise<RestResponse>;
|
300 | try {
|
301 | const baseUri: string | undefined = operationSpec.baseUrl || this.baseUri;
|
302 | if (!baseUri) {
|
303 | throw new Error(
|
304 | "If operationSpec.baseUrl is not specified, then the ServiceClient must have a baseUri string property that contains the base URL to use."
|
305 | );
|
306 | }
|
307 |
|
308 | httpRequest.method = operationSpec.httpMethod;
|
309 | httpRequest.operationSpec = operationSpec;
|
310 |
|
311 | const requestUrl: URLBuilder = URLBuilder.parse(baseUri);
|
312 | if (operationSpec.path) {
|
313 | requestUrl.appendPath(operationSpec.path);
|
314 | }
|
315 | if (operationSpec.urlParameters && operationSpec.urlParameters.length > 0) {
|
316 | for (const urlParameter of operationSpec.urlParameters) {
|
317 | let urlParameterValue: string = getOperationArgumentValueFromParameter(
|
318 | this,
|
319 | operationArguments,
|
320 | urlParameter,
|
321 | operationSpec.serializer
|
322 | );
|
323 | urlParameterValue = operationSpec.serializer.serialize(
|
324 | urlParameter.mapper,
|
325 | urlParameterValue,
|
326 | getPathStringFromParameter(urlParameter)
|
327 | );
|
328 | if (!urlParameter.skipEncoding) {
|
329 | urlParameterValue = encodeURIComponent(urlParameterValue);
|
330 | }
|
331 | requestUrl.replaceAll(
|
332 | `{${urlParameter.mapper.serializedName || getPathStringFromParameter(urlParameter)}}`,
|
333 | urlParameterValue
|
334 | );
|
335 | }
|
336 | }
|
337 | if (operationSpec.queryParameters && operationSpec.queryParameters.length > 0) {
|
338 | for (const queryParameter of operationSpec.queryParameters) {
|
339 | let queryParameterValue: any = getOperationArgumentValueFromParameter(
|
340 | this,
|
341 | operationArguments,
|
342 | queryParameter,
|
343 | operationSpec.serializer
|
344 | );
|
345 | if (queryParameterValue != undefined) {
|
346 | queryParameterValue = operationSpec.serializer.serialize(
|
347 | queryParameter.mapper,
|
348 | queryParameterValue,
|
349 | getPathStringFromParameter(queryParameter)
|
350 | );
|
351 | if (queryParameter.collectionFormat != undefined) {
|
352 | if (queryParameter.collectionFormat === QueryCollectionFormat.Multi) {
|
353 | if (queryParameterValue.length === 0) {
|
354 | queryParameterValue = "";
|
355 | } else {
|
356 | for (const index in queryParameterValue) {
|
357 | const item = queryParameterValue[index];
|
358 | queryParameterValue[index] = item == undefined ? "" : item.toString();
|
359 | }
|
360 | }
|
361 | } else if (
|
362 | queryParameter.collectionFormat === QueryCollectionFormat.Ssv ||
|
363 | queryParameter.collectionFormat === QueryCollectionFormat.Tsv
|
364 | ) {
|
365 | queryParameterValue = queryParameterValue.join(queryParameter.collectionFormat);
|
366 | }
|
367 | }
|
368 | if (!queryParameter.skipEncoding) {
|
369 | if (Array.isArray(queryParameterValue)) {
|
370 | for (const index in queryParameterValue) {
|
371 | if (
|
372 | queryParameterValue[index] !== undefined &&
|
373 | queryParameterValue[index] !== null
|
374 | ) {
|
375 | queryParameterValue[index] = encodeURIComponent(queryParameterValue[index]);
|
376 | }
|
377 | }
|
378 | } else {
|
379 | queryParameterValue = encodeURIComponent(queryParameterValue);
|
380 | }
|
381 | }
|
382 | if (
|
383 | queryParameter.collectionFormat != undefined &&
|
384 | queryParameter.collectionFormat !== QueryCollectionFormat.Multi &&
|
385 | queryParameter.collectionFormat !== QueryCollectionFormat.Ssv &&
|
386 | queryParameter.collectionFormat !== QueryCollectionFormat.Tsv
|
387 | ) {
|
388 | queryParameterValue = queryParameterValue.join(queryParameter.collectionFormat);
|
389 | }
|
390 | requestUrl.setQueryParameter(
|
391 | queryParameter.mapper.serializedName || getPathStringFromParameter(queryParameter),
|
392 | queryParameterValue
|
393 | );
|
394 | }
|
395 | }
|
396 | }
|
397 | httpRequest.url = requestUrl.toString();
|
398 |
|
399 | const contentType = operationSpec.contentType || this.requestContentType;
|
400 | if (contentType) {
|
401 | httpRequest.headers.set("Content-Type", contentType);
|
402 | }
|
403 |
|
404 | if (operationSpec.headerParameters) {
|
405 | for (const headerParameter of operationSpec.headerParameters) {
|
406 | let headerValue: any = getOperationArgumentValueFromParameter(
|
407 | this,
|
408 | operationArguments,
|
409 | headerParameter,
|
410 | operationSpec.serializer
|
411 | );
|
412 | if (headerValue != undefined) {
|
413 | headerValue = operationSpec.serializer.serialize(
|
414 | headerParameter.mapper,
|
415 | headerValue,
|
416 | getPathStringFromParameter(headerParameter)
|
417 | );
|
418 | const headerCollectionPrefix = (headerParameter.mapper as DictionaryMapper)
|
419 | .headerCollectionPrefix;
|
420 | if (headerCollectionPrefix) {
|
421 | for (const key of Object.keys(headerValue)) {
|
422 | httpRequest.headers.set(headerCollectionPrefix + key, headerValue[key]);
|
423 | }
|
424 | } else {
|
425 | httpRequest.headers.set(
|
426 | headerParameter.mapper.serializedName ||
|
427 | getPathStringFromParameter(headerParameter),
|
428 | headerValue
|
429 | );
|
430 | }
|
431 | }
|
432 | }
|
433 | }
|
434 |
|
435 | const options: RequestOptionsBase | undefined = operationArguments.options;
|
436 | if (options) {
|
437 | if (options.customHeaders) {
|
438 | for (const customHeaderName in options.customHeaders) {
|
439 | httpRequest.headers.set(customHeaderName, options.customHeaders[customHeaderName]);
|
440 | }
|
441 | }
|
442 |
|
443 | if (options.abortSignal) {
|
444 | httpRequest.abortSignal = options.abortSignal;
|
445 | }
|
446 |
|
447 | if (options.timeout) {
|
448 | httpRequest.timeout = options.timeout;
|
449 | }
|
450 |
|
451 | if (options.onUploadProgress) {
|
452 | httpRequest.onUploadProgress = options.onUploadProgress;
|
453 | }
|
454 |
|
455 | if (options.onDownloadProgress) {
|
456 | httpRequest.onDownloadProgress = options.onDownloadProgress;
|
457 | }
|
458 | }
|
459 |
|
460 | httpRequest.withCredentials = this._withCredentials;
|
461 |
|
462 | serializeRequestBody(this, httpRequest, operationArguments, operationSpec);
|
463 |
|
464 | if (httpRequest.streamResponseBody == undefined) {
|
465 | httpRequest.streamResponseBody = isStreamOperation(operationSpec);
|
466 | }
|
467 |
|
468 | result = this.sendRequest(httpRequest).then((res) =>
|
469 | flattenResponse(res, operationSpec.responses[res.status])
|
470 | );
|
471 | } catch (error) {
|
472 | result = Promise.reject(error);
|
473 | }
|
474 |
|
475 | const cb = callback;
|
476 | if (cb) {
|
477 | result
|
478 | // tslint:disable-next-line:no-null-keyword
|
479 | .then((res) => cb(null, res._response.parsedBody, res._response.request, res._response))
|
480 | .catch((err) => cb(err));
|
481 | }
|
482 |
|
483 | return result;
|
484 | }
|
485 | }
|
486 |
|
487 | export function serializeRequestBody(
|
488 | serviceClient: ServiceClient,
|
489 | httpRequest: WebResourceLike,
|
490 | operationArguments: OperationArguments,
|
491 | operationSpec: OperationSpec
|
492 | ): void {
|
493 | if (operationSpec.requestBody && operationSpec.requestBody.mapper) {
|
494 | httpRequest.body = getOperationArgumentValueFromParameter(
|
495 | serviceClient,
|
496 | operationArguments,
|
497 | operationSpec.requestBody,
|
498 | operationSpec.serializer
|
499 | );
|
500 |
|
501 | const bodyMapper = operationSpec.requestBody.mapper;
|
502 | const { required, xmlName, xmlElementName, serializedName } = bodyMapper;
|
503 | const typeName = bodyMapper.type.name;
|
504 | try {
|
505 | if (httpRequest.body != undefined || required) {
|
506 | const requestBodyParameterPathString: string = getPathStringFromParameter(
|
507 | operationSpec.requestBody
|
508 | );
|
509 | httpRequest.body = operationSpec.serializer.serialize(
|
510 | bodyMapper,
|
511 | httpRequest.body,
|
512 | requestBodyParameterPathString
|
513 | );
|
514 | const isStream = typeName === MapperType.Stream;
|
515 | if (operationSpec.isXML) {
|
516 | if (typeName === MapperType.Sequence) {
|
517 | httpRequest.body = stringifyXML(
|
518 | utils.prepareXMLRootList(
|
519 | httpRequest.body,
|
520 | xmlElementName || xmlName || serializedName!
|
521 | ),
|
522 | { rootName: xmlName || serializedName }
|
523 | );
|
524 | } else if (!isStream) {
|
525 | httpRequest.body = stringifyXML(httpRequest.body, {
|
526 | rootName: xmlName || serializedName,
|
527 | });
|
528 | }
|
529 | } else if (!isStream) {
|
530 | httpRequest.body = JSON.stringify(httpRequest.body);
|
531 | }
|
532 | }
|
533 | } catch (error) {
|
534 | throw new Error(
|
535 | `Error "${error.message}" occurred in serializing the payload - ${JSON.stringify(
|
536 | serializedName,
|
537 | undefined,
|
538 | " "
|
539 | )}.`
|
540 | );
|
541 | }
|
542 | } else if (operationSpec.formDataParameters && operationSpec.formDataParameters.length > 0) {
|
543 | httpRequest.formData = {};
|
544 | for (const formDataParameter of operationSpec.formDataParameters) {
|
545 | const formDataParameterValue: any = getOperationArgumentValueFromParameter(
|
546 | serviceClient,
|
547 | operationArguments,
|
548 | formDataParameter,
|
549 | operationSpec.serializer
|
550 | );
|
551 | if (formDataParameterValue != undefined) {
|
552 | const formDataParameterPropertyName: string =
|
553 | formDataParameter.mapper.serializedName || getPathStringFromParameter(formDataParameter);
|
554 | httpRequest.formData[formDataParameterPropertyName] = operationSpec.serializer.serialize(
|
555 | formDataParameter.mapper,
|
556 | formDataParameterValue,
|
557 | getPathStringFromParameter(formDataParameter)
|
558 | );
|
559 | }
|
560 | }
|
561 | }
|
562 | }
|
563 |
|
564 | function isRequestPolicyFactory(instance: any): instance is RequestPolicyFactory {
|
565 | return typeof instance.create === "function";
|
566 | }
|
567 |
|
568 | function getValueOrFunctionResult(
|
569 | value: undefined | string | ((defaultValue: string) => string),
|
570 | defaultValueCreator: () => string
|
571 | ): string {
|
572 | let result: string;
|
573 | if (typeof value === "string") {
|
574 | result = value;
|
575 | } else {
|
576 | result = defaultValueCreator();
|
577 | if (typeof value === "function") {
|
578 | result = value(result);
|
579 | }
|
580 | }
|
581 | return result;
|
582 | }
|
583 |
|
584 | function createDefaultRequestPolicyFactories(
|
585 | credentials: ServiceClientCredentials | RequestPolicyFactory | undefined,
|
586 | options: ServiceClientOptions
|
587 | ): RequestPolicyFactory[] {
|
588 | const factories: RequestPolicyFactory[] = [];
|
589 |
|
590 | if (options.generateClientRequestIdHeader) {
|
591 | factories.push(generateClientRequestIdPolicy(options.clientRequestIdHeaderName));
|
592 | }
|
593 |
|
594 | if (credentials) {
|
595 | if (isRequestPolicyFactory(credentials)) {
|
596 | factories.push(credentials);
|
597 | } else {
|
598 | factories.push(signingPolicy(credentials));
|
599 | }
|
600 | }
|
601 |
|
602 | const userAgentHeaderName: string = getValueOrFunctionResult(
|
603 | options.userAgentHeaderName,
|
604 | getDefaultUserAgentHeaderName
|
605 | );
|
606 | const userAgentHeaderValue: string = getValueOrFunctionResult(
|
607 | options.userAgent,
|
608 | getDefaultUserAgentValue
|
609 | );
|
610 | if (userAgentHeaderName && userAgentHeaderValue) {
|
611 | factories.push(userAgentPolicy({ key: userAgentHeaderName, value: userAgentHeaderValue }));
|
612 | }
|
613 |
|
614 | const redirectOptions = {
|
615 | ...DefaultRedirectOptions,
|
616 | ...options.redirectOptions,
|
617 | };
|
618 | if (redirectOptions.handleRedirects) {
|
619 | factories.push(redirectPolicy(redirectOptions.maxRetries));
|
620 | }
|
621 |
|
622 | factories.push(rpRegistrationPolicy(options.rpRegistrationRetryTimeout));
|
623 |
|
624 | if (!options.noRetryPolicy) {
|
625 | factories.push(exponentialRetryPolicy());
|
626 | factories.push(systemErrorRetryPolicy());
|
627 | factories.push(throttlingRetryPolicy());
|
628 | }
|
629 |
|
630 | factories.push(deserializationPolicy(options.deserializationContentTypes));
|
631 |
|
632 | const proxySettings = options.proxySettings || getDefaultProxySettings();
|
633 | if (proxySettings) {
|
634 | factories.push(proxyPolicy(proxySettings));
|
635 | }
|
636 |
|
637 | if (options.agentSettings) {
|
638 | factories.push(agentPolicy(options.agentSettings));
|
639 | }
|
640 |
|
641 | return factories;
|
642 | }
|
643 |
|
644 | export type PropertyParent = { [propertyName: string]: any };
|
645 |
|
646 | /**
|
647 | * Get the property parent for the property at the provided path when starting with the provided
|
648 | * parent object.
|
649 | */
|
650 | export function getPropertyParent(parent: PropertyParent, propertyPath: string[]): PropertyParent {
|
651 | if (parent && propertyPath) {
|
652 | const propertyPathLength: number = propertyPath.length;
|
653 | for (let i = 0; i < propertyPathLength - 1; ++i) {
|
654 | const propertyName: string = propertyPath[i];
|
655 | if (!parent[propertyName]) {
|
656 | parent[propertyName] = {};
|
657 | }
|
658 | parent = parent[propertyName];
|
659 | }
|
660 | }
|
661 | return parent;
|
662 | }
|
663 |
|
664 | function getOperationArgumentValueFromParameter(
|
665 | serviceClient: ServiceClient,
|
666 | operationArguments: OperationArguments,
|
667 | parameter: OperationParameter,
|
668 | serializer: Serializer
|
669 | ): any {
|
670 | return getOperationArgumentValueFromParameterPath(
|
671 | serviceClient,
|
672 | operationArguments,
|
673 | parameter.parameterPath,
|
674 | parameter.mapper,
|
675 | serializer
|
676 | );
|
677 | }
|
678 |
|
679 | export function getOperationArgumentValueFromParameterPath(
|
680 | serviceClient: ServiceClient,
|
681 | operationArguments: OperationArguments,
|
682 | parameterPath: ParameterPath,
|
683 | parameterMapper: Mapper,
|
684 | serializer: Serializer
|
685 | ): any {
|
686 | let value: any;
|
687 | if (typeof parameterPath === "string") {
|
688 | parameterPath = [parameterPath];
|
689 | }
|
690 | if (Array.isArray(parameterPath)) {
|
691 | if (parameterPath.length > 0) {
|
692 | if (parameterMapper.isConstant) {
|
693 | value = parameterMapper.defaultValue;
|
694 | } else {
|
695 | let propertySearchResult: PropertySearchResult = getPropertyFromParameterPath(
|
696 | operationArguments,
|
697 | parameterPath
|
698 | );
|
699 | if (!propertySearchResult.propertyFound) {
|
700 | propertySearchResult = getPropertyFromParameterPath(serviceClient, parameterPath);
|
701 | }
|
702 |
|
703 | let useDefaultValue = false;
|
704 | if (!propertySearchResult.propertyFound) {
|
705 | useDefaultValue =
|
706 | parameterMapper.required ||
|
707 | (parameterPath[0] === "options" && parameterPath.length === 2);
|
708 | }
|
709 | value = useDefaultValue ? parameterMapper.defaultValue : propertySearchResult.propertyValue;
|
710 | }
|
711 |
|
712 | // Serialize just for validation purposes.
|
713 | const parameterPathString: string = getPathStringFromParameterPath(
|
714 | parameterPath,
|
715 | parameterMapper
|
716 | );
|
717 | serializer.serialize(parameterMapper, value, parameterPathString);
|
718 | }
|
719 | } else {
|
720 | if (parameterMapper.required) {
|
721 | value = {};
|
722 | }
|
723 |
|
724 | for (const propertyName in parameterPath) {
|
725 | const propertyMapper: Mapper = (parameterMapper as CompositeMapper).type.modelProperties![
|
726 | propertyName
|
727 | ];
|
728 | const propertyPath: ParameterPath = parameterPath[propertyName];
|
729 | const propertyValue: any = getOperationArgumentValueFromParameterPath(
|
730 | serviceClient,
|
731 | operationArguments,
|
732 | propertyPath,
|
733 | propertyMapper,
|
734 | serializer
|
735 | );
|
736 | // Serialize just for validation purposes.
|
737 | const propertyPathString: string = getPathStringFromParameterPath(
|
738 | propertyPath,
|
739 | propertyMapper
|
740 | );
|
741 | serializer.serialize(propertyMapper, propertyValue, propertyPathString);
|
742 | if (propertyValue !== undefined) {
|
743 | if (!value) {
|
744 | value = {};
|
745 | }
|
746 | value[propertyName] = propertyValue;
|
747 | }
|
748 | }
|
749 | }
|
750 | return value;
|
751 | }
|
752 |
|
753 | interface PropertySearchResult {
|
754 | propertyValue?: any;
|
755 | propertyFound: boolean;
|
756 | }
|
757 |
|
758 | function getPropertyFromParameterPath(
|
759 | parent: { [parameterName: string]: any },
|
760 | parameterPath: string[]
|
761 | ): PropertySearchResult {
|
762 | const result: PropertySearchResult = { propertyFound: false };
|
763 | let i = 0;
|
764 | for (; i < parameterPath.length; ++i) {
|
765 | const parameterPathPart: string = parameterPath[i];
|
766 | // Make sure to check inherited properties too, so don't use hasOwnProperty().
|
767 | if (parent != undefined && parameterPathPart in parent) {
|
768 | parent = parent[parameterPathPart];
|
769 | } else {
|
770 | break;
|
771 | }
|
772 | }
|
773 | if (i === parameterPath.length) {
|
774 | result.propertyValue = parent;
|
775 | result.propertyFound = true;
|
776 | }
|
777 | return result;
|
778 | }
|
779 |
|
780 | export function flattenResponse(
|
781 | _response: HttpOperationResponse,
|
782 | responseSpec: OperationResponse | undefined
|
783 | ): RestResponse {
|
784 | const parsedHeaders = _response.parsedHeaders;
|
785 | const bodyMapper = responseSpec && responseSpec.bodyMapper;
|
786 |
|
787 | const addOperationResponse = (obj: {}) =>
|
788 | Object.defineProperty(obj, "_response", {
|
789 | value: _response,
|
790 | });
|
791 |
|
792 | if (bodyMapper) {
|
793 | const typeName = bodyMapper.type.name;
|
794 | if (typeName === "Stream") {
|
795 | return addOperationResponse({
|
796 | ...parsedHeaders,
|
797 | blobBody: _response.blobBody,
|
798 | readableStreamBody: _response.readableStreamBody,
|
799 | });
|
800 | }
|
801 |
|
802 | const modelProperties =
|
803 | (typeName === "Composite" && (bodyMapper as CompositeMapper).type.modelProperties) || {};
|
804 | const isPageableResponse = Object.keys(modelProperties).some(
|
805 | (k) => modelProperties[k].serializedName === ""
|
806 | );
|
807 | if (typeName === "Sequence" || isPageableResponse) {
|
808 |
|
809 |
|
810 | const parsedBody = Array.isArray(_response.parsedBody) ? _response.parsedBody : [];
|
811 | const arrayResponse = [...parsedBody] as RestResponse & any[];
|
812 |
|
813 | for (const key of Object.keys(modelProperties)) {
|
814 | if (modelProperties[key].serializedName) {
|
815 | arrayResponse[key] = _response.parsedBody[key];
|
816 | }
|
817 | }
|
818 |
|
819 | if (parsedHeaders) {
|
820 | for (const key of Object.keys(parsedHeaders)) {
|
821 | arrayResponse[key] = parsedHeaders[key];
|
822 | }
|
823 | }
|
824 | addOperationResponse(arrayResponse);
|
825 | return arrayResponse;
|
826 | }
|
827 |
|
828 | if (typeName === "Composite" || typeName === "Dictionary") {
|
829 | return addOperationResponse({
|
830 | ...parsedHeaders,
|
831 | ..._response.parsedBody,
|
832 | });
|
833 | }
|
834 | }
|
835 |
|
836 | if (
|
837 | bodyMapper ||
|
838 | _response.request.method === "HEAD" ||
|
839 | utils.isPrimitiveType(_response.parsedBody)
|
840 | ) {
|
841 |
|
842 | return addOperationResponse({
|
843 | ...parsedHeaders,
|
844 | body: _response.parsedBody,
|
845 | });
|
846 | }
|
847 |
|
848 | return addOperationResponse({
|
849 | ...parsedHeaders,
|
850 | ..._response.parsedBody,
|
851 | });
|
852 | }
|