serviceClient.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See License.txt in the project root for license information.
  3. import { ServiceClientCredentials } from "./credentials/serviceClientCredentials";
  4. import { DefaultHttpClient } from "./defaultHttpClient";
  5. import { HttpClient } from "./httpClient";
  6. import { HttpOperationResponse, RestResponse } from "./httpOperationResponse";
  7. import { HttpPipelineLogger } from "./httpPipelineLogger";
  8. import { OperationArguments } from "./operationArguments";
  9. import { getPathStringFromParameter, getPathStringFromParameterPath, OperationParameter, ParameterPath } from "./operationParameter";
  10. import { isStreamOperation, OperationSpec } from "./operationSpec";
  11. import { deserializationPolicy, DeserializationContentTypes } from "./policies/deserializationPolicy";
  12. import { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
  13. import { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
  14. import { userAgentPolicy, getDefaultUserAgentHeaderName, getDefaultUserAgentValue } from "./policies/userAgentPolicy";
  15. import { redirectPolicy } from "./policies/redirectPolicy";
  16. import { RequestPolicy, RequestPolicyFactory, RequestPolicyOptions } from "./policies/requestPolicy";
  17. import { rpRegistrationPolicy } from "./policies/rpRegistrationPolicy";
  18. import { signingPolicy } from "./policies/signingPolicy";
  19. import { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
  20. import { QueryCollectionFormat } from "./queryCollectionFormat";
  21. import { CompositeMapper, DictionaryMapper, Mapper, MapperType, Serializer } from "./serializer";
  22. import { URLBuilder } from "./url";
  23. import * as utils from "./util/utils";
  24. import { stringifyXML } from "./util/xml";
  25. import { RequestOptionsBase, RequestPrepareOptions, WebResource } from "./webResource";
  26. import { OperationResponse } from "./operationResponse";
  27. import { ServiceCallback } from "./util/utils";
  28. import { proxyPolicy, getDefaultProxySettings } from "./policies/proxyPolicy";
  29. import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy";
  30. /**
  31. * HTTP proxy settings (Node.js only)
  32. */
  33. export interface ProxySettings {
  34. host: string;
  35. port: number;
  36. username?: string;
  37. password?: string;
  38. }
  39. /**
  40. * Options to be provided while creating the client.
  41. */
  42. export interface ServiceClientOptions {
  43. /**
  44. * An array of factories which get called to create the RequestPolicy pipeline used to send a HTTP
  45. * request on the wire, or a function that takes in the defaultRequestPolicyFactories and returns
  46. * the requestPolicyFactories that will be used.
  47. */
  48. requestPolicyFactories?: RequestPolicyFactory[] | ((defaultRequestPolicyFactories: RequestPolicyFactory[]) => (void | RequestPolicyFactory[]));
  49. /**
  50. * The HttpClient that will be used to send HTTP requests.
  51. */
  52. httpClient?: HttpClient;
  53. /**
  54. * The HttpPipelineLogger that can be used to debug RequestPolicies within the HTTP pipeline.
  55. */
  56. httpPipelineLogger?: HttpPipelineLogger;
  57. /**
  58. * If set to true, turn off the default retry policy.
  59. */
  60. noRetryPolicy?: boolean;
  61. /**
  62. * Gets or sets the retry timeout in seconds for AutomaticRPRegistration. Default value is 30.
  63. */
  64. rpRegistrationRetryTimeout?: number;
  65. /**
  66. * Whether or not to generate a client request ID header for each HTTP request.
  67. */
  68. generateClientRequestIdHeader?: boolean;
  69. /**
  70. * Whether to include credentials in CORS requests in the browser.
  71. * See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials for more information.
  72. */
  73. withCredentials?: boolean;
  74. /**
  75. * If specified, a GenerateRequestIdPolicy will be added to the HTTP pipeline that will add a
  76. * header to all outgoing requests with this header name and a random UUID as the request ID.
  77. */
  78. clientRequestIdHeaderName?: string;
  79. /**
  80. * The content-types that will be associated with JSON or XML serialization.
  81. */
  82. deserializationContentTypes?: DeserializationContentTypes;
  83. /**
  84. * The header name to use for the telemetry header while sending the request. If this is not
  85. * specified, then "User-Agent" will be used when running on Node.js and "x-ms-command-name" will
  86. * be used when running in a browser.
  87. */
  88. userAgentHeaderName?: string | ((defaultUserAgentHeaderName: string) => string);
  89. /**
  90. * The string to be set to the telemetry header while sending the request, or a function that
  91. * takes in the default user-agent string and returns the user-agent string that will be used.
  92. */
  93. userAgent?: string | ((defaultUserAgent: string) => string);
  94. /**
  95. * Proxy settings which will be used for every HTTP request (Node.js only).
  96. */
  97. proxySettings?: ProxySettings;
  98. }
  99. /**
  100. * @class
  101. * Initializes a new instance of the ServiceClient.
  102. */
  103. export class ServiceClient {
  104. /**
  105. * If specified, this is the base URI that requests will be made against for this ServiceClient.
  106. * If it is not specified, then all OperationSpecs must contain a baseUrl property.
  107. */
  108. protected baseUri?: string;
  109. /**
  110. * The default request content type for the service.
  111. * Used if no requestContentType is present on an OperationSpec.
  112. */
  113. protected requestContentType?: string;
  114. /**
  115. * The HTTP client that will be used to send requests.
  116. */
  117. private readonly _httpClient: HttpClient;
  118. private readonly _requestPolicyOptions: RequestPolicyOptions;
  119. private readonly _requestPolicyFactories: RequestPolicyFactory[];
  120. private readonly _withCredentials: boolean;
  121. /**
  122. * The ServiceClient constructor
  123. * @constructor
  124. * @param {ServiceClientCredentials} [credentials] The credentials object used for authentication.
  125. * @param {ServiceClientOptions} [options] The service client options that govern the behavior of the client.
  126. */
  127. constructor(credentials?: ServiceClientCredentials, options?: ServiceClientOptions) {
  128. if (!options) {
  129. options = {};
  130. }
  131. if (credentials && !credentials.signRequest) {
  132. throw new Error("credentials argument needs to implement signRequest method");
  133. }
  134. this._withCredentials = options.withCredentials || false;
  135. this._httpClient = options.httpClient || new DefaultHttpClient();
  136. this._requestPolicyOptions = new RequestPolicyOptions(options.httpPipelineLogger);
  137. let requestPolicyFactories: RequestPolicyFactory[];
  138. if (Array.isArray(options.requestPolicyFactories)) {
  139. requestPolicyFactories = options.requestPolicyFactories;
  140. } else {
  141. requestPolicyFactories = createDefaultRequestPolicyFactories(credentials, options);
  142. if (options.requestPolicyFactories) {
  143. const newRequestPolicyFactories: void | RequestPolicyFactory[] = options.requestPolicyFactories(requestPolicyFactories);
  144. if (newRequestPolicyFactories) {
  145. requestPolicyFactories = newRequestPolicyFactories;
  146. }
  147. }
  148. }
  149. this._requestPolicyFactories = requestPolicyFactories;
  150. }
  151. /**
  152. * Send the provided httpRequest.
  153. */
  154. sendRequest(options: RequestPrepareOptions | WebResource): Promise<HttpOperationResponse> {
  155. if (options === null || options === undefined || typeof options !== "object") {
  156. throw new Error("options cannot be null or undefined and it must be of type object.");
  157. }
  158. let httpRequest: WebResource;
  159. try {
  160. if (options instanceof WebResource) {
  161. options.validateRequestProperties();
  162. httpRequest = options;
  163. } else {
  164. httpRequest = new WebResource();
  165. httpRequest = httpRequest.prepare(options);
  166. }
  167. } catch (error) {
  168. return Promise.reject(error);
  169. }
  170. let httpPipeline: RequestPolicy = this._httpClient;
  171. if (this._requestPolicyFactories && this._requestPolicyFactories.length > 0) {
  172. for (let i = this._requestPolicyFactories.length - 1; i >= 0; --i) {
  173. httpPipeline = this._requestPolicyFactories[i].create(httpPipeline, this._requestPolicyOptions);
  174. }
  175. }
  176. return httpPipeline.sendRequest(httpRequest);
  177. }
  178. /**
  179. * Send an HTTP request that is populated using the provided OperationSpec.
  180. * @param {OperationArguments} operationArguments The arguments that the HTTP request's templated values will be populated from.
  181. * @param {OperationSpec} operationSpec The OperationSpec to use to populate the httpRequest.
  182. * @param {ServiceCallback} callback The callback to call when the response is received.
  183. */
  184. sendOperationRequest(operationArguments: OperationArguments, operationSpec: OperationSpec, callback?: ServiceCallback<any>): Promise<RestResponse> {
  185. if (typeof operationArguments.options === "function") {
  186. callback = operationArguments.options;
  187. operationArguments.options = undefined;
  188. }
  189. const httpRequest = new WebResource();
  190. let result: Promise<RestResponse>;
  191. try {
  192. const baseUri: string | undefined = operationSpec.baseUrl || this.baseUri;
  193. if (!baseUri) {
  194. throw new Error("If operationSpec.baseUrl is not specified, then the ServiceClient must have a baseUri string property that contains the base URL to use.");
  195. }
  196. httpRequest.method = operationSpec.httpMethod;
  197. httpRequest.operationSpec = operationSpec;
  198. const requestUrl: URLBuilder = URLBuilder.parse(baseUri);
  199. if (operationSpec.path) {
  200. requestUrl.appendPath(operationSpec.path);
  201. }
  202. if (operationSpec.urlParameters && operationSpec.urlParameters.length > 0) {
  203. for (const urlParameter of operationSpec.urlParameters) {
  204. let urlParameterValue: string = getOperationArgumentValueFromParameter(this, operationArguments, urlParameter, operationSpec.serializer);
  205. urlParameterValue = operationSpec.serializer.serialize(urlParameter.mapper, urlParameterValue, getPathStringFromParameter(urlParameter));
  206. if (!urlParameter.skipEncoding) {
  207. urlParameterValue = encodeURIComponent(urlParameterValue);
  208. }
  209. requestUrl.replaceAll(`{${urlParameter.mapper.serializedName || getPathStringFromParameter(urlParameter)}}`, urlParameterValue);
  210. }
  211. }
  212. if (operationSpec.queryParameters && operationSpec.queryParameters.length > 0) {
  213. for (const queryParameter of operationSpec.queryParameters) {
  214. let queryParameterValue: any = getOperationArgumentValueFromParameter(this, operationArguments, queryParameter, operationSpec.serializer);
  215. if (queryParameterValue != undefined) {
  216. queryParameterValue = operationSpec.serializer.serialize(queryParameter.mapper, queryParameterValue, getPathStringFromParameter(queryParameter));
  217. if (queryParameter.collectionFormat != undefined) {
  218. if (queryParameter.collectionFormat === QueryCollectionFormat.Multi) {
  219. if (queryParameterValue.length === 0) {
  220. queryParameterValue = "";
  221. } else {
  222. for (const index in queryParameterValue) {
  223. const item = queryParameterValue[index];
  224. queryParameterValue[index] = item == undefined ? "" : item.toString();
  225. }
  226. }
  227. } else {
  228. queryParameterValue = queryParameterValue.join(queryParameter.collectionFormat);
  229. }
  230. }
  231. if (!queryParameter.skipEncoding) {
  232. if (Array.isArray(queryParameterValue)) {
  233. for (const index in queryParameterValue) {
  234. queryParameterValue[index] = encodeURIComponent(queryParameterValue[index]);
  235. }
  236. }
  237. else {
  238. queryParameterValue = encodeURIComponent(queryParameterValue);
  239. }
  240. }
  241. requestUrl.setQueryParameter(queryParameter.mapper.serializedName || getPathStringFromParameter(queryParameter), queryParameterValue);
  242. }
  243. }
  244. }
  245. httpRequest.url = requestUrl.toString();
  246. const contentType = operationSpec.contentType || this.requestContentType;
  247. if (contentType) {
  248. httpRequest.headers.set("Content-Type", contentType);
  249. }
  250. if (operationSpec.headerParameters) {
  251. for (const headerParameter of operationSpec.headerParameters) {
  252. let headerValue: any = getOperationArgumentValueFromParameter(this, operationArguments, headerParameter, operationSpec.serializer);
  253. if (headerValue != undefined) {
  254. headerValue = operationSpec.serializer.serialize(headerParameter.mapper, headerValue, getPathStringFromParameter(headerParameter));
  255. const headerCollectionPrefix = (headerParameter.mapper as DictionaryMapper).headerCollectionPrefix;
  256. if (headerCollectionPrefix) {
  257. for (const key of Object.keys(headerValue)) {
  258. httpRequest.headers.set(headerCollectionPrefix + key, headerValue[key]);
  259. }
  260. } else {
  261. httpRequest.headers.set(headerParameter.mapper.serializedName || getPathStringFromParameter(headerParameter), headerValue);
  262. }
  263. }
  264. }
  265. }
  266. const options: RequestOptionsBase | undefined = operationArguments.options;
  267. if (options) {
  268. if (options.customHeaders) {
  269. for (const customHeaderName in options.customHeaders) {
  270. httpRequest.headers.set(customHeaderName, options.customHeaders[customHeaderName]);
  271. }
  272. }
  273. if (options.abortSignal) {
  274. httpRequest.abortSignal = options.abortSignal;
  275. }
  276. if (options.timeout) {
  277. httpRequest.timeout = options.timeout;
  278. }
  279. if (options.onUploadProgress) {
  280. httpRequest.onUploadProgress = options.onUploadProgress;
  281. }
  282. if (options.onDownloadProgress) {
  283. httpRequest.onDownloadProgress = options.onDownloadProgress;
  284. }
  285. }
  286. httpRequest.withCredentials = this._withCredentials;
  287. serializeRequestBody(this, httpRequest, operationArguments, operationSpec);
  288. if (httpRequest.streamResponseBody == undefined) {
  289. httpRequest.streamResponseBody = isStreamOperation(operationSpec);
  290. }
  291. result = this.sendRequest(httpRequest)
  292. .then(res => flattenResponse(res, operationSpec.responses[res.status]));
  293. } catch (error) {
  294. result = Promise.reject(error);
  295. }
  296. const cb = callback;
  297. if (cb) {
  298. result
  299. // tslint:disable-next-line:no-null-keyword
  300. .then(res => cb(null, res._response.parsedBody, res._response.request, res._response))
  301. .catch(err => cb(err));
  302. }
  303. return result;
  304. }
  305. }
  306. export function serializeRequestBody(serviceClient: ServiceClient, httpRequest: WebResource, operationArguments: OperationArguments, operationSpec: OperationSpec): void {
  307. if (operationSpec.requestBody && operationSpec.requestBody.mapper) {
  308. httpRequest.body = getOperationArgumentValueFromParameter(serviceClient, operationArguments, operationSpec.requestBody, operationSpec.serializer);
  309. const bodyMapper = operationSpec.requestBody.mapper;
  310. const { required, xmlName, xmlElementName, serializedName } = bodyMapper;
  311. const typeName = bodyMapper.type.name;
  312. try {
  313. if (httpRequest.body != undefined || required) {
  314. const requestBodyParameterPathString: string = getPathStringFromParameter(operationSpec.requestBody);
  315. httpRequest.body = operationSpec.serializer.serialize(bodyMapper, httpRequest.body, requestBodyParameterPathString);
  316. const isStream = typeName === MapperType.Stream;
  317. if (operationSpec.isXML) {
  318. if (typeName === MapperType.Sequence) {
  319. httpRequest.body = stringifyXML(utils.prepareXMLRootList(httpRequest.body, xmlElementName || xmlName || serializedName!), { rootName: xmlName || serializedName });
  320. }
  321. else if (!isStream) {
  322. httpRequest.body = stringifyXML(httpRequest.body, { rootName: xmlName || serializedName });
  323. }
  324. } else if (!isStream) {
  325. httpRequest.body = JSON.stringify(httpRequest.body);
  326. }
  327. }
  328. } catch (error) {
  329. throw new Error(`Error "${error.message}" occurred in serializing the payload - ${JSON.stringify(serializedName, undefined, " ")}.`);
  330. }
  331. } else if (operationSpec.formDataParameters && operationSpec.formDataParameters.length > 0) {
  332. httpRequest.formData = {};
  333. for (const formDataParameter of operationSpec.formDataParameters) {
  334. const formDataParameterValue: any = getOperationArgumentValueFromParameter(serviceClient, operationArguments, formDataParameter, operationSpec.serializer);
  335. if (formDataParameterValue != undefined) {
  336. const formDataParameterPropertyName: string = formDataParameter.mapper.serializedName || getPathStringFromParameter(formDataParameter);
  337. httpRequest.formData[formDataParameterPropertyName] = operationSpec.serializer.serialize(formDataParameter.mapper, formDataParameterValue, getPathStringFromParameter(formDataParameter));
  338. }
  339. }
  340. }
  341. }
  342. function isRequestPolicyFactory(instance: any): instance is RequestPolicyFactory {
  343. return typeof instance.create === "function";
  344. }
  345. function getValueOrFunctionResult(value: undefined | string | ((defaultValue: string) => string), defaultValueCreator: (() => string)): string {
  346. let result: string;
  347. if (typeof value === "string") {
  348. result = value;
  349. } else {
  350. result = defaultValueCreator();
  351. if (typeof value === "function") {
  352. result = value(result);
  353. }
  354. }
  355. return result;
  356. }
  357. function createDefaultRequestPolicyFactories(credentials: ServiceClientCredentials | RequestPolicyFactory | undefined, options: ServiceClientOptions): RequestPolicyFactory[] {
  358. const factories: RequestPolicyFactory[] = [];
  359. if (options.generateClientRequestIdHeader) {
  360. factories.push(generateClientRequestIdPolicy(options.clientRequestIdHeaderName));
  361. }
  362. if (credentials) {
  363. if (isRequestPolicyFactory(credentials)) {
  364. factories.push(credentials);
  365. } else {
  366. factories.push(signingPolicy(credentials));
  367. }
  368. }
  369. const userAgentHeaderName: string = getValueOrFunctionResult(options.userAgentHeaderName, getDefaultUserAgentHeaderName);
  370. const userAgentHeaderValue: string = getValueOrFunctionResult(options.userAgent, getDefaultUserAgentValue);
  371. if (userAgentHeaderName && userAgentHeaderValue) {
  372. factories.push(userAgentPolicy({ key: userAgentHeaderName, value: userAgentHeaderValue }));
  373. }
  374. factories.push(redirectPolicy());
  375. factories.push(rpRegistrationPolicy(options.rpRegistrationRetryTimeout));
  376. if (!options.noRetryPolicy) {
  377. factories.push(exponentialRetryPolicy());
  378. factories.push(systemErrorRetryPolicy());
  379. factories.push(throttlingRetryPolicy());
  380. }
  381. factories.push(deserializationPolicy(options.deserializationContentTypes));
  382. const proxySettings = options.proxySettings || getDefaultProxySettings();
  383. if (proxySettings) {
  384. factories.push(proxyPolicy(proxySettings));
  385. }
  386. return factories;
  387. }
  388. export type PropertyParent = { [propertyName: string]: any };
  389. /**
  390. * Get the property parent for the property at the provided path when starting with the provided
  391. * parent object.
  392. */
  393. export function getPropertyParent(parent: PropertyParent, propertyPath: string[]): PropertyParent {
  394. if (parent && propertyPath) {
  395. const propertyPathLength: number = propertyPath.length;
  396. for (let i = 0; i < propertyPathLength - 1; ++i) {
  397. const propertyName: string = propertyPath[i];
  398. if (!parent[propertyName]) {
  399. parent[propertyName] = {};
  400. }
  401. parent = parent[propertyName];
  402. }
  403. }
  404. return parent;
  405. }
  406. function getOperationArgumentValueFromParameter(serviceClient: ServiceClient, operationArguments: OperationArguments, parameter: OperationParameter, serializer: Serializer): any {
  407. return getOperationArgumentValueFromParameterPath(serviceClient, operationArguments, parameter.parameterPath, parameter.mapper, serializer);
  408. }
  409. export function getOperationArgumentValueFromParameterPath(serviceClient: ServiceClient, operationArguments: OperationArguments, parameterPath: ParameterPath, parameterMapper: Mapper, serializer: Serializer): any {
  410. let value: any;
  411. if (typeof parameterPath === "string") {
  412. parameterPath = [parameterPath];
  413. }
  414. if (Array.isArray(parameterPath)) {
  415. if (parameterPath.length > 0) {
  416. if (parameterMapper.isConstant) {
  417. value = parameterMapper.defaultValue;
  418. } else {
  419. let propertySearchResult: PropertySearchResult = getPropertyFromParameterPath(operationArguments, parameterPath);
  420. if (!propertySearchResult.propertyFound) {
  421. propertySearchResult = getPropertyFromParameterPath(serviceClient, parameterPath);
  422. }
  423. let useDefaultValue = false;
  424. if (!propertySearchResult.propertyFound) {
  425. useDefaultValue = parameterMapper.required || (parameterPath[0] === "options" && parameterPath.length === 2);
  426. }
  427. value = useDefaultValue ? parameterMapper.defaultValue : propertySearchResult.propertyValue;
  428. }
  429. // Serialize just for validation purposes.
  430. const parameterPathString: string = getPathStringFromParameterPath(parameterPath, parameterMapper);
  431. serializer.serialize(parameterMapper, value, parameterPathString);
  432. }
  433. } else {
  434. if (parameterMapper.required) {
  435. value = {};
  436. }
  437. for (const propertyName in parameterPath) {
  438. const propertyMapper: Mapper = (parameterMapper as CompositeMapper).type.modelProperties![propertyName];
  439. const propertyPath: ParameterPath = parameterPath[propertyName];
  440. const propertyValue: any = getOperationArgumentValueFromParameterPath(serviceClient, operationArguments, propertyPath, propertyMapper, serializer);
  441. // Serialize just for validation purposes.
  442. const propertyPathString: string = getPathStringFromParameterPath(propertyPath, propertyMapper);
  443. serializer.serialize(propertyMapper, propertyValue, propertyPathString);
  444. if (propertyValue !== undefined) {
  445. if (!value) {
  446. value = {};
  447. }
  448. value[propertyName] = propertyValue;
  449. }
  450. }
  451. }
  452. return value;
  453. }
  454. interface PropertySearchResult {
  455. propertyValue?: any;
  456. propertyFound: boolean;
  457. }
  458. function getPropertyFromParameterPath(parent: { [parameterName: string]: any }, parameterPath: string[]): PropertySearchResult {
  459. const result: PropertySearchResult = { propertyFound: false };
  460. let i = 0;
  461. for (; i < parameterPath.length; ++i) {
  462. const parameterPathPart: string = parameterPath[i];
  463. // Make sure to check inherited properties too, so don't use hasOwnProperty().
  464. if (parent != undefined && parameterPathPart in parent) {
  465. parent = parent[parameterPathPart];
  466. } else {
  467. break;
  468. }
  469. }
  470. if (i === parameterPath.length) {
  471. result.propertyValue = parent;
  472. result.propertyFound = true;
  473. }
  474. return result;
  475. }
  476. export function flattenResponse(_response: HttpOperationResponse, responseSpec: OperationResponse | undefined): RestResponse {
  477. const parsedHeaders = _response.parsedHeaders;
  478. const bodyMapper = responseSpec && responseSpec.bodyMapper;
  479. const addOperationResponse = (obj: {}) =>
  480. Object.defineProperty(obj, "_response", {
  481. value: _response
  482. });
  483. if (bodyMapper) {
  484. const typeName = bodyMapper.type.name;
  485. if (typeName === "Stream") {
  486. return addOperationResponse({
  487. ...parsedHeaders,
  488. blobBody: _response.blobBody,
  489. readableStreamBody: _response.readableStreamBody
  490. });
  491. }
  492. const modelProperties = typeName === "Composite" && (bodyMapper as CompositeMapper).type.modelProperties || {};
  493. const isPageableResponse = Object.keys(modelProperties).some(k => modelProperties[k].serializedName === "");
  494. if (typeName === "Sequence" || isPageableResponse) {
  495. const arrayResponse = [...(_response.parsedBody || [])] as RestResponse & any[];
  496. for (const key of Object.keys(modelProperties)) {
  497. if (modelProperties[key].serializedName) {
  498. arrayResponse[key] = _response.parsedBody[key];
  499. }
  500. }
  501. if (parsedHeaders) {
  502. for (const key of Object.keys(parsedHeaders)) {
  503. arrayResponse[key] = parsedHeaders[key];
  504. }
  505. }
  506. addOperationResponse(arrayResponse);
  507. return arrayResponse;
  508. }
  509. if (typeName === "Composite" || typeName === "Dictionary") {
  510. return addOperationResponse({
  511. ...parsedHeaders,
  512. ..._response.parsedBody
  513. });
  514. }
  515. }
  516. if (bodyMapper || _response.request.method === "HEAD") {
  517. // primitive body types and HEAD booleans
  518. return addOperationResponse({
  519. ...parsedHeaders,
  520. body: _response.parsedBody
  521. });
  522. }
  523. return addOperationResponse({
  524. ...parsedHeaders,
  525. ..._response.parsedBody
  526. });
  527. }