feat: add @actions/cache

This commit is contained in:
xHyroM 2022-07-12 09:00:22 +02:00
parent b15fb7d098
commit 16e8c96a41
1932 changed files with 261172 additions and 10 deletions

View file

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import {
CommonRequestInfo,
CommonRequestInit,
CommonResponse,
FetchHttpClient,
} from "./fetchHttpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResourceLike } from "./webResource";
export class BrowserFetchHttpClient extends FetchHttpClient {
prepareRequest(_httpRequest: WebResourceLike): Promise<Partial<RequestInit>> {
return Promise.resolve({});
}
processRequest(_operationResponse: HttpOperationResponse): Promise<void> {
return Promise.resolve();
}
fetch(input: CommonRequestInfo, init?: CommonRequestInit): Promise<CommonResponse> {
return fetch(input, init);
}
}

View file

@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpHeaders } from "../httpHeaders";
import { WebResourceLike } from "../webResource";
import { ServiceClientCredentials } from "./serviceClientCredentials";
/**
* @interface ApiKeyCredentialOptions
* Describes the options to be provided while creating an instance of ApiKeyCredentials
*/
export interface ApiKeyCredentialOptions {
/**
* A key value pair of the header parameters that need to be applied to the request.
*/
inHeader?: { [x: string]: any };
/**
* A key value pair of the query parameters that need to be applied to the request.
*/
inQuery?: { [x: string]: any };
}
/**
* Authenticates to a service using an API key.
*/
export class ApiKeyCredentials implements ServiceClientCredentials {
/**
* A key value pair of the header parameters that need to be applied to the request.
*/
private readonly inHeader?: { [x: string]: any };
/**
* A key value pair of the query parameters that need to be applied to the request.
*/
private readonly inQuery?: { [x: string]: any };
/**
* @constructor
* @param {object} options Specifies the options to be provided for auth. Either header or query needs to be provided.
*/
constructor(options: ApiKeyCredentialOptions) {
if (!options || (options && !options.inHeader && !options.inQuery)) {
throw new Error(
`options cannot be null or undefined. Either "inHeader" or "inQuery" property of the options object needs to be provided.`
);
}
this.inHeader = options.inHeader;
this.inQuery = options.inQuery;
}
/**
* Signs a request with the values provided in the inHeader and inQuery parameter.
*
* @param {WebResource} webResource The WebResource to be signed.
* @returns {Promise<WebResource>} The signed request object.
*/
signRequest(webResource: WebResourceLike): Promise<WebResourceLike> {
if (!webResource) {
return Promise.reject(
new Error(`webResource cannot be null or undefined and must be of type "object".`)
);
}
if (this.inHeader) {
if (!webResource.headers) {
webResource.headers = new HttpHeaders();
}
for (const headerName in this.inHeader) {
webResource.headers.set(headerName, this.inHeader[headerName]);
}
}
if (this.inQuery) {
if (!webResource.url) {
return Promise.reject(new Error(`url cannot be null in the request object.`));
}
if (webResource.url.indexOf("?") < 0) {
webResource.url += "?";
}
for (const key in this.inQuery) {
if (!webResource.url.endsWith("?")) {
webResource.url += "&";
}
webResource.url += `${key}=${this.inQuery[key]}`;
}
}
return Promise.resolve(webResource);
}
}

View file

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ServiceClientCredentials } from "./serviceClientCredentials";
import { Constants as MSRestConstants } from "../util/constants";
import { WebResource } from "../webResource";
import { TokenCredential } from "@azure/core-auth";
import { TokenResponse } from "./tokenResponse";
const DEFAULT_AUTHORIZATION_SCHEME = "Bearer";
/**
* Resource manager endpoints to match in order to specify a valid scope to the AzureIdentityCredentialAdapter.
*/
export const azureResourceManagerEndpoints = [
"https://management.windows.net",
"https://management.chinacloudapi.cn",
"https://management.usgovcloudapi.net",
"https://management.cloudapi.de",
];
/**
* This class provides a simple extension to use {@link TokenCredential} from `@azure/identity` library to
* use with legacy Azure SDKs that accept {@link ServiceClientCredentials} family of credentials for authentication.
*/
export class AzureIdentityCredentialAdapter implements ServiceClientCredentials {
private azureTokenCredential: TokenCredential;
private scopes: string | string[];
constructor(
azureTokenCredential: TokenCredential,
scopes: string | string[] = "https://management.azure.com/.default"
) {
this.azureTokenCredential = azureTokenCredential;
this.scopes = scopes;
}
public async getToken(): Promise<TokenResponse> {
const accessToken = await this.azureTokenCredential.getToken(this.scopes);
if (accessToken !== null) {
const result: TokenResponse = {
accessToken: accessToken.token,
tokenType: DEFAULT_AUTHORIZATION_SCHEME,
expiresOn: accessToken.expiresOnTimestamp,
};
return result;
} else {
throw new Error("Could find token for scope");
}
}
public async signRequest(webResource: WebResource) {
const tokenResponse = await this.getToken();
webResource.headers.set(
MSRestConstants.HeaderConstants.AUTHORIZATION,
`${tokenResponse.tokenType} ${tokenResponse.accessToken}`
);
return Promise.resolve(webResource);
}
}

View file

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpHeaders } from "../httpHeaders";
import * as base64 from "../util/base64";
import { Constants } from "../util/constants";
import { WebResourceLike } from "../webResource";
import { ServiceClientCredentials } from "./serviceClientCredentials";
const HeaderConstants = Constants.HeaderConstants;
const DEFAULT_AUTHORIZATION_SCHEME = "Basic";
export class BasicAuthenticationCredentials implements ServiceClientCredentials {
userName: string;
password: string;
authorizationScheme: string = DEFAULT_AUTHORIZATION_SCHEME;
/**
* Creates a new BasicAuthenticationCredentials object.
*
* @constructor
* @param {string} userName User name.
* @param {string} password Password.
* @param {string} [authorizationScheme] The authorization scheme.
*/
constructor(
userName: string,
password: string,
authorizationScheme: string = DEFAULT_AUTHORIZATION_SCHEME
) {
if (userName === null || userName === undefined || typeof userName.valueOf() !== "string") {
throw new Error("userName cannot be null or undefined and must be of type string.");
}
if (password === null || password === undefined || typeof password.valueOf() !== "string") {
throw new Error("password cannot be null or undefined and must be of type string.");
}
this.userName = userName;
this.password = password;
this.authorizationScheme = authorizationScheme;
}
/**
* Signs a request with the Authentication header.
*
* @param {WebResourceLike} webResource The WebResourceLike to be signed.
* @returns {Promise<WebResourceLike>} The signed request object.
*/
signRequest(webResource: WebResourceLike) {
const credentials = `${this.userName}:${this.password}`;
const encodedCredentials = `${this.authorizationScheme} ${base64.encodeString(credentials)}`;
if (!webResource.headers) webResource.headers = new HttpHeaders();
webResource.headers.set(HeaderConstants.AUTHORIZATION, encodedCredentials);
return Promise.resolve(webResource);
}
}

View file

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
export type Authenticator = (challenge: object) => Promise<string>;

View file

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ApiKeyCredentials, ApiKeyCredentialOptions } from "./apiKeyCredentials";
export class DomainCredentials extends ApiKeyCredentials {
/**
* Creates a new EventGrid DomainCredentials object.
*
* @constructor
* @param {string} domainKey The EventGrid domain key
*/
constructor(domainKey: string) {
if (!domainKey || (domainKey && typeof domainKey !== "string")) {
throw new Error("domainKey cannot be null or undefined and must be of type string.");
}
const options: ApiKeyCredentialOptions = {
inHeader: {
"aeg-sas-key": domainKey,
},
};
super(options);
}
}

View file

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { WebResourceLike } from "../webResource";
export interface ServiceClientCredentials {
/**
* Signs a request with the Authentication header.
*
* @param {WebResourceLike} webResource The WebResourceLike/request to be signed.
* @returns {Promise<WebResourceLike>} The signed request object;
*/
signRequest(webResource: WebResourceLike): Promise<WebResourceLike>;
}

View file

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpHeaders } from "../httpHeaders";
import { Constants } from "../util/constants";
import { WebResourceLike } from "../webResource";
import { ServiceClientCredentials } from "./serviceClientCredentials";
const HeaderConstants = Constants.HeaderConstants;
const DEFAULT_AUTHORIZATION_SCHEME = "Bearer";
/**
* A credentials object that uses a token string and a authorzation scheme to authenticate.
*/
export class TokenCredentials implements ServiceClientCredentials {
token: string;
authorizationScheme: string = DEFAULT_AUTHORIZATION_SCHEME;
/**
* Creates a new TokenCredentials object.
*
* @constructor
* @param {string} token The token.
* @param {string} [authorizationScheme] The authorization scheme.
*/
constructor(token: string, authorizationScheme: string = DEFAULT_AUTHORIZATION_SCHEME) {
if (!token) {
throw new Error("token cannot be null or undefined.");
}
this.token = token;
this.authorizationScheme = authorizationScheme;
}
/**
* Signs a request with the Authentication header.
*
* @param {WebResourceLike} webResource The WebResourceLike to be signed.
* @return {Promise<WebResourceLike>} The signed request object.
*/
signRequest(webResource: WebResourceLike) {
if (!webResource.headers) webResource.headers = new HttpHeaders();
webResource.headers.set(
HeaderConstants.AUTHORIZATION,
`${this.authorizationScheme} ${this.token}`
);
return Promise.resolve(webResource);
}
}

View file

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* TokenResponse is defined in `@azure/ms-rest-nodeauth` and is copied here to not
* add an unnecessary dependency.
*/
export interface TokenResponse {
readonly tokenType: string;
readonly accessToken: string;
readonly [x: string]: any;
}

View file

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ApiKeyCredentials, ApiKeyCredentialOptions } from "./apiKeyCredentials";
export class TopicCredentials extends ApiKeyCredentials {
/**
* Creates a new EventGrid TopicCredentials object.
*
* @constructor
* @param {string} topicKey The EventGrid topic key
*/
constructor(topicKey: string) {
if (!topicKey || (topicKey && typeof topicKey !== "string")) {
throw new Error("topicKey cannot be null or undefined and must be of type string.");
}
const options: ApiKeyCredentialOptions = {
inHeader: {
"aeg-sas-key": topicKey,
},
};
super(options);
}
}

View file

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
export { XhrHttpClient as DefaultHttpClient } from "./xhrHttpClient";

View file

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
export { NodeFetchHttpClient as DefaultHttpClient } from "./nodeFetchHttpClient";

256
node_modules/@azure/ms-rest-js/lib/fetchHttpClient.ts generated vendored Normal file
View file

@ -0,0 +1,256 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import AbortController from "abort-controller";
import FormData from "form-data";
import { HttpClient } from "./httpClient";
import { WebResourceLike } from "./webResource";
import { HttpOperationResponse } from "./httpOperationResponse";
import { HttpHeaders, HttpHeadersLike } from "./httpHeaders";
import { RestError } from "./restError";
import { Readable, Transform } from "stream";
interface FetchError extends Error {
code?: string;
errno?: string;
type?: string;
}
export type CommonRequestInfo = string; // we only call fetch() on string urls.
export type CommonRequestInit = Omit<RequestInit, "body" | "headers" | "signal"> & {
body?: any;
headers?: any;
signal?: any;
};
export type CommonResponse = Omit<Response, "body" | "trailer" | "formData"> & {
body: any;
trailer: any;
formData: any;
};
export abstract class FetchHttpClient implements HttpClient {
async sendRequest(httpRequest: WebResourceLike): Promise<HttpOperationResponse> {
if (!httpRequest && typeof httpRequest !== "object") {
throw new Error(
"'httpRequest' (WebResource) cannot be null or undefined and must be of type object."
);
}
const abortController = new AbortController();
let abortListener: ((event: any) => void) | undefined;
if (httpRequest.abortSignal) {
if (httpRequest.abortSignal.aborted) {
throw new RestError(
"The request was aborted",
RestError.REQUEST_ABORTED_ERROR,
undefined,
httpRequest
);
}
abortListener = (event: Event) => {
if (event.type === "abort") {
abortController.abort();
}
};
httpRequest.abortSignal.addEventListener("abort", abortListener);
}
if (httpRequest.timeout) {
setTimeout(() => {
abortController.abort();
}, httpRequest.timeout);
}
if (httpRequest.formData) {
const formData: any = httpRequest.formData;
const requestForm = new FormData();
const appendFormValue = (key: string, value: any) => {
// value function probably returns a stream so we can provide a fresh stream on each retry
if (typeof value === "function") {
value = value();
}
if (value && value.hasOwnProperty("value") && value.hasOwnProperty("options")) {
requestForm.append(key, value.value, value.options);
} else {
requestForm.append(key, value);
}
};
for (const formKey of Object.keys(formData)) {
const formValue = formData[formKey];
if (Array.isArray(formValue)) {
for (let j = 0; j < formValue.length; j++) {
appendFormValue(formKey, formValue[j]);
}
} else {
appendFormValue(formKey, formValue);
}
}
httpRequest.body = requestForm;
httpRequest.formData = undefined;
const contentType = httpRequest.headers.get("Content-Type");
if (contentType && contentType.indexOf("multipart/form-data") !== -1) {
if (typeof requestForm.getBoundary === "function") {
httpRequest.headers.set(
"Content-Type",
`multipart/form-data; boundary=${requestForm.getBoundary()}`
);
} else {
// browser will automatically apply a suitable content-type header
httpRequest.headers.remove("Content-Type");
}
}
}
let body = httpRequest.body
? typeof httpRequest.body === "function"
? httpRequest.body()
: httpRequest.body
: undefined;
if (httpRequest.onUploadProgress && httpRequest.body) {
let loadedBytes = 0;
const uploadReportStream = new Transform({
transform: (chunk: string | Buffer, _encoding, callback) => {
loadedBytes += chunk.length;
httpRequest.onUploadProgress!({ loadedBytes });
callback(undefined, chunk);
},
});
if (isReadableStream(body)) {
body.pipe(uploadReportStream);
} else {
uploadReportStream.end(body);
}
body = uploadReportStream;
}
const platformSpecificRequestInit: Partial<RequestInit> = await this.prepareRequest(
httpRequest
);
const requestInit: RequestInit = {
body: body,
headers: httpRequest.headers.rawHeaders(),
method: httpRequest.method,
signal: abortController.signal,
redirect: "manual",
...platformSpecificRequestInit,
};
let operationResponse: HttpOperationResponse | undefined;
try {
const response: CommonResponse = await this.fetch(httpRequest.url, requestInit);
const headers = parseHeaders(response.headers);
operationResponse = {
headers: headers,
request: httpRequest,
status: response.status,
readableStreamBody: httpRequest.streamResponseBody
? ((response.body as unknown) as NodeJS.ReadableStream)
: undefined,
bodyAsText: !httpRequest.streamResponseBody ? await response.text() : undefined,
redirected: response.redirected,
url: response.url,
};
const onDownloadProgress = httpRequest.onDownloadProgress;
if (onDownloadProgress) {
const responseBody: ReadableStream<Uint8Array> | undefined = response.body || undefined;
if (isReadableStream(responseBody)) {
let loadedBytes = 0;
const downloadReportStream = new Transform({
transform: (chunk: string | Buffer, _encoding, callback) => {
loadedBytes += chunk.length;
onDownloadProgress({ loadedBytes });
callback(undefined, chunk);
},
});
responseBody.pipe(downloadReportStream);
operationResponse.readableStreamBody = downloadReportStream;
} else {
const length = parseInt(headers.get("Content-Length")!) || undefined;
if (length) {
// Calling callback for non-stream response for consistency with browser
onDownloadProgress({ loadedBytes: length });
}
}
}
await this.processRequest(operationResponse);
return operationResponse;
} catch (error) {
const fetchError: FetchError = error;
if (fetchError.code === "ENOTFOUND") {
throw new RestError(
fetchError.message,
RestError.REQUEST_SEND_ERROR,
undefined,
httpRequest
);
} else if (fetchError.type === "aborted") {
throw new RestError(
"The request was aborted",
RestError.REQUEST_ABORTED_ERROR,
undefined,
httpRequest
);
}
throw fetchError;
} finally {
// clean up event listener
if (httpRequest.abortSignal && abortListener) {
let uploadStreamDone = Promise.resolve();
if (isReadableStream(body)) {
uploadStreamDone = isStreamComplete(body);
}
let downloadStreamDone = Promise.resolve();
if (isReadableStream(operationResponse?.readableStreamBody)) {
downloadStreamDone = isStreamComplete(operationResponse!.readableStreamBody);
}
Promise.all([uploadStreamDone, downloadStreamDone])
.then(() => {
httpRequest.abortSignal?.removeEventListener("abort", abortListener!);
return;
})
.catch((_e) => {});
}
}
}
abstract async prepareRequest(httpRequest: WebResourceLike): Promise<Partial<RequestInit>>;
abstract async processRequest(operationResponse: HttpOperationResponse): Promise<void>;
abstract async fetch(input: CommonRequestInfo, init?: CommonRequestInit): Promise<CommonResponse>;
}
function isReadableStream(body: any): body is Readable {
return body && typeof body.pipe === "function";
}
function isStreamComplete(stream: Readable): Promise<void> {
return new Promise((resolve) => {
stream.on("close", resolve);
stream.on("end", resolve);
stream.on("error", resolve);
});
}
export function parseHeaders(headers: Headers): HttpHeadersLike {
const httpHeaders = new HttpHeaders();
headers.forEach((value, key) => {
httpHeaders.set(key, value);
});
return httpHeaders;
}

9
node_modules/@azure/ms-rest-js/lib/httpClient.ts generated vendored Normal file
View file

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { RequestPolicy } from "./policies/requestPolicy";
/**
* An interface that can send HttpRequests and receive promised HttpResponses.
*/
export interface HttpClient extends RequestPolicy {}

230
node_modules/@azure/ms-rest-js/lib/httpHeaders.ts generated vendored Normal file
View file

@ -0,0 +1,230 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* A collection of HttpHeaders that can be sent with a HTTP request.
*/
function getHeaderKey(headerName: string) {
return headerName.toLowerCase();
}
/**
* An individual header within a HttpHeaders collection.
*/
export interface HttpHeader {
/**
* The name of the header.
*/
name: string;
/**
* The value of the header.
*/
value: string;
}
/**
* A HttpHeaders collection represented as a simple JSON object.
*/
export type RawHttpHeaders = { [headerName: string]: string };
/**
* A collection of HTTP header key/value pairs.
*/
export interface HttpHeadersLike {
/**
* Set a header in this collection with the provided name and value. The name is
* case-insensitive.
* @param headerName The name of the header to set. This value is case-insensitive.
* @param headerValue The value of the header to set.
*/
set(headerName: string, headerValue: string | number): void;
/**
* Get the header value for the provided header name, or undefined if no header exists in this
* collection with the provided name.
* @param headerName The name of the header.
*/
get(headerName: string): string | undefined;
/**
* Get whether or not this header collection contains a header entry for the provided header name.
*/
contains(headerName: string): boolean;
/**
* Remove the header with the provided headerName. Return whether or not the header existed and
* was removed.
* @param headerName The name of the header to remove.
*/
remove(headerName: string): boolean;
/**
* Get the headers that are contained this collection as an object.
*/
rawHeaders(): RawHttpHeaders;
/**
* Get the headers that are contained in this collection as an array.
*/
headersArray(): HttpHeader[];
/**
* Get the header names that are contained in this collection.
*/
headerNames(): string[];
/**
* Get the header values that are contained in this collection.
*/
headerValues(): string[];
/**
* Create a deep clone/copy of this HttpHeaders collection.
*/
clone(): HttpHeadersLike;
/**
* Get the JSON object representation of this HTTP header collection.
* The result is the same as `rawHeaders()`.
*/
toJson(): RawHttpHeaders;
}
export function isHttpHeadersLike(object?: any): object is HttpHeadersLike {
if (!object || typeof object !== "object") {
return false;
}
if (
typeof object.rawHeaders === "function" &&
typeof object.clone === "function" &&
typeof object.get === "function" &&
typeof object.set === "function" &&
typeof object.contains === "function" &&
typeof object.remove === "function" &&
typeof object.headersArray === "function" &&
typeof object.headerValues === "function" &&
typeof object.headerNames === "function" &&
typeof object.toJson === "function"
) {
return true;
}
return false;
}
/**
* A collection of HTTP header key/value pairs.
*/
export class HttpHeaders {
private readonly _headersMap: { [headerKey: string]: HttpHeader };
constructor(rawHeaders?: RawHttpHeaders) {
this._headersMap = {};
if (rawHeaders) {
for (const headerName in rawHeaders) {
this.set(headerName, rawHeaders[headerName]);
}
}
}
/**
* Set a header in this collection with the provided name and value. The name is
* case-insensitive.
* @param headerName The name of the header to set. This value is case-insensitive.
* @param headerValue The value of the header to set.
*/
public set(headerName: string, headerValue: string | number): void {
this._headersMap[getHeaderKey(headerName)] = {
name: headerName,
value: headerValue.toString(),
};
}
/**
* Get the header value for the provided header name, or undefined if no header exists in this
* collection with the provided name.
* @param headerName The name of the header.
*/
public get(headerName: string): string | undefined {
const header: HttpHeader = this._headersMap[getHeaderKey(headerName)];
return !header ? undefined : header.value;
}
/**
* Get whether or not this header collection contains a header entry for the provided header name.
*/
public contains(headerName: string): boolean {
return !!this._headersMap[getHeaderKey(headerName)];
}
/**
* Remove the header with the provided headerName. Return whether or not the header existed and
* was removed.
* @param headerName The name of the header to remove.
*/
public remove(headerName: string): boolean {
const result: boolean = this.contains(headerName);
delete this._headersMap[getHeaderKey(headerName)];
return result;
}
/**
* Get the headers that are contained this collection as an object.
*/
public rawHeaders(): RawHttpHeaders {
const result: RawHttpHeaders = {};
for (const headerKey in this._headersMap) {
const header: HttpHeader = this._headersMap[headerKey];
result[header.name.toLowerCase()] = header.value;
}
return result;
}
/**
* Get the headers that are contained in this collection as an array.
*/
public headersArray(): HttpHeader[] {
const headers: HttpHeader[] = [];
for (const headerKey in this._headersMap) {
headers.push(this._headersMap[headerKey]);
}
return headers;
}
/**
* Get the header names that are contained in this collection.
*/
public headerNames(): string[] {
const headerNames: string[] = [];
const headers: HttpHeader[] = this.headersArray();
for (let i = 0; i < headers.length; ++i) {
headerNames.push(headers[i].name);
}
return headerNames;
}
/**
* Get the header names that are contained in this collection.
*/
public headerValues(): string[] {
const headerValues: string[] = [];
const headers: HttpHeader[] = this.headersArray();
for (let i = 0; i < headers.length; ++i) {
headerValues.push(headers[i].value);
}
return headerValues;
}
/**
* Get the JSON object representation of this HTTP header collection.
*/
public toJson(): RawHttpHeaders {
return this.rawHeaders();
}
/**
* Get the string representation of this HTTP header collection.
*/
public toString(): string {
return JSON.stringify(this.toJson());
}
/**
* Create a deep clone/copy of this HttpHeaders collection.
*/
public clone(): HttpHeaders {
return new HttpHeaders(this.rawHeaders());
}
}

View file

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { WebResourceLike } from "./webResource";
import { HttpHeadersLike } from "./httpHeaders";
/**
* The properties on an HTTP response which will always be present.
*/
export interface HttpResponse {
/**
* The raw request
*/
request: WebResourceLike;
/**
* The HTTP response status (e.g. 200)
*/
status: number;
/**
* The HTTP response headers.
*/
headers: HttpHeadersLike;
}
declare global {
/**
* Stub declaration of the browser-only Blob type.
* Full type information can be obtained by including "lib": ["dom"] in tsconfig.json.
*/
interface Blob {}
}
/**
* Wrapper object for http request and response. Deserialized object is stored in
* the `parsedBody` property when the response body is received in JSON or XML.
*/
export interface HttpOperationResponse extends HttpResponse {
/**
* The parsed HTTP response headers.
*/
parsedHeaders?: { [key: string]: any };
/**
* The response body as text (string format)
*/
bodyAsText?: string | null;
/**
* The response body as parsed JSON or XML
*/
parsedBody?: any;
/**
* BROWSER ONLY
*
* The response body as a browser Blob.
* Always undefined in node.js.
*/
blobBody?: Promise<Blob>;
/**
* NODEJS ONLY
*
* The response body as a node.js Readable stream.
* Always undefined in the browser.
*/
readableStreamBody?: NodeJS.ReadableStream;
/**
* The redirected property indicates whether the response is the result of a request which was redirected.
*/
redirected?: boolean;
/**
* The url property contains the URL of the response. The value will be the final URL obtained after any redirects.
*/
url?: string;
}
/**
* The flattened response to a REST call.
* Contains the underlying HttpOperationResponse as well as
* the merged properties of the parsedBody, parsedHeaders, etc.
*/
export interface RestResponse {
/**
* The underlying HTTP response containing both raw and deserialized response data.
*/
_response: HttpOperationResponse;
[key: string]: any;
}

View file

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* The different levels of logs that can be used with the HttpPipelineLogger.
*/
export enum HttpPipelineLogLevel {
/**
* A log level that indicates that no logs will be logged.
*/
OFF,
/**
* An error log.
*/
ERROR,
/**
* A warning log.
*/
WARNING,
/**
* An information log.
*/
INFO,
}

View file

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpPipelineLogLevel } from "./httpPipelineLogLevel";
/**
* A Logger that can be added to a HttpPipeline. This enables each RequestPolicy to log messages
* that can be used for debugging purposes.
*/
export interface HttpPipelineLogger {
/**
* The log level threshold for what logs will be logged.
*/
minimumLogLevel: HttpPipelineLogLevel;
/**
* Log the provided message.
* @param logLevel The HttpLogDetailLevel associated with this message.
* @param message The message to log.
*/
log(logLevel: HttpPipelineLogLevel, message: string): void;
}
/**
* A HttpPipelineLogger that will send its logs to the console.
*/
export class ConsoleHttpPipelineLogger implements HttpPipelineLogger {
/**
* Create a new ConsoleHttpPipelineLogger.
* @param minimumLogLevel The log level threshold for what logs will be logged.
*/
constructor(public minimumLogLevel: HttpPipelineLogLevel) {}
/**
* Log the provided message.
* @param logLevel The HttpLogDetailLevel associated with this message.
* @param message The message to log.
*/
log(logLevel: HttpPipelineLogLevel, message: string): void {
const logMessage = `${HttpPipelineLogLevel[logLevel]}: ${message}`;
switch (logLevel) {
case HttpPipelineLogLevel.ERROR:
console.error(logMessage);
break;
case HttpPipelineLogLevel.WARNING:
console.warn(logMessage);
break;
case HttpPipelineLogLevel.INFO:
console.log(logMessage);
break;
}
}
}

114
node_modules/@azure/ms-rest-js/lib/msRest.ts generated vendored Normal file
View file

@ -0,0 +1,114 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/// <reference path="../dom-shim.d.ts" />
export {
WebResource,
WebResourceLike,
HttpRequestBody,
RequestPrepareOptions,
HttpMethods,
ParameterValue,
RequestOptionsBase,
TransferProgressEvent,
AbortSignalLike,
} from "./webResource";
export { DefaultHttpClient } from "./defaultHttpClient";
export { CommonRequestInfo, CommonRequestInit, CommonResponse } from "./fetchHttpClient";
export { HttpClient } from "./httpClient";
export { HttpHeader, HttpHeaders, HttpHeadersLike, RawHttpHeaders } from "./httpHeaders";
export { HttpOperationResponse, HttpResponse, RestResponse } from "./httpOperationResponse";
export { HttpPipelineLogger } from "./httpPipelineLogger";
export { HttpPipelineLogLevel } from "./httpPipelineLogLevel";
export { RestError } from "./restError";
export { OperationArguments } from "./operationArguments";
export {
OperationParameter,
OperationQueryParameter,
OperationURLParameter,
ParameterPath,
} from "./operationParameter";
export { OperationResponse } from "./operationResponse";
export { OperationSpec } from "./operationSpec";
export {
AgentSettings,
ProxySettings,
ServiceClient,
ServiceClientOptions,
flattenResponse,
} from "./serviceClient";
export { QueryCollectionFormat } from "./queryCollectionFormat";
export { Constants } from "./util/constants";
export { logPolicy } from "./policies/logPolicy";
export {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptions,
RequestPolicyOptionsLike,
} from "./policies/requestPolicy";
export { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
export { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
export { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
export { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy";
export { agentPolicy } from "./policies/agentPolicy";
export { getDefaultProxySettings, proxyPolicy } from "./policies/proxyPolicy";
export { RedirectOptions, redirectPolicy } from "./policies/redirectPolicy";
export { signingPolicy } from "./policies/signingPolicy";
export {
TelemetryInfo,
userAgentPolicy,
getDefaultUserAgentValue,
} from "./policies/userAgentPolicy";
export {
DeserializationContentTypes,
deserializationPolicy,
deserializeResponseBody,
} from "./policies/deserializationPolicy";
export {
MapperType,
SimpleMapperType,
CompositeMapperType,
DictionaryMapperType,
SequenceMapperType,
EnumMapperType,
Mapper,
BaseMapper,
CompositeMapper,
SequenceMapper,
DictionaryMapper,
EnumMapper,
MapperConstraints,
PolymorphicDiscriminator,
Serializer,
UrlParameterValue,
serializeObject,
} from "./serializer";
export {
stripRequest,
stripResponse,
delay,
executePromisesSequentially,
generateUuid,
encodeUri,
ServiceCallback,
promiseToCallback,
promiseToServiceCallback,
isValidUuid,
applyMixins,
isNode,
isDuration,
} from "./util/utils";
export { URLBuilder, URLQuery } from "./url";
// Credentials
export { TokenCredentials } from "./credentials/tokenCredentials";
export { TokenResponse } from "./credentials/tokenResponse";
export { BasicAuthenticationCredentials } from "./credentials/basicAuthenticationCredentials";
export { ApiKeyCredentials, ApiKeyCredentialOptions } from "./credentials/apiKeyCredentials";
export { ServiceClientCredentials } from "./credentials/serviceClientCredentials";
export { TopicCredentials } from "./credentials/topicCredentials";
export { DomainCredentials } from "./credentials/domainCredentials";
export { Authenticator } from "./credentials/credentials";
export { AzureIdentityCredentialAdapter } from "./credentials/azureIdentityTokenCredentialAdapter";

View file

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as tough from "tough-cookie";
import * as http from "http";
import * as https from "https";
import node_fetch from "node-fetch";
import {
CommonRequestInfo,
CommonRequestInit,
CommonResponse,
FetchHttpClient,
} from "./fetchHttpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResourceLike } from "./webResource";
import { createProxyAgent, ProxyAgent } from "./proxyAgent";
export class NodeFetchHttpClient extends FetchHttpClient {
private readonly cookieJar = new tough.CookieJar(undefined, { looseMode: true });
async fetch(input: CommonRequestInfo, init?: CommonRequestInit): Promise<CommonResponse> {
return (node_fetch(input, init) as unknown) as Promise<CommonResponse>;
}
async prepareRequest(httpRequest: WebResourceLike): Promise<Partial<RequestInit>> {
const requestInit: Partial<RequestInit & { agent?: any }> = {};
if (this.cookieJar && !httpRequest.headers.get("Cookie")) {
const cookieString = await new Promise<string>((resolve, reject) => {
this.cookieJar!.getCookieString(httpRequest.url, (err, cookie) => {
if (err) {
reject(err);
} else {
resolve(cookie);
}
});
});
httpRequest.headers.set("Cookie", cookieString);
}
if (httpRequest.agentSettings) {
const { http: httpAgent, https: httpsAgent } = httpRequest.agentSettings;
if (httpsAgent && httpRequest.url.startsWith("https")) {
requestInit.agent = httpsAgent;
} else if (httpAgent) {
requestInit.agent = httpAgent;
}
} else if (httpRequest.proxySettings) {
const tunnel: ProxyAgent = createProxyAgent(
httpRequest.url,
httpRequest.proxySettings,
httpRequest.headers
);
requestInit.agent = tunnel.agent;
}
if (httpRequest.keepAlive === true) {
if (requestInit.agent) {
requestInit.agent.keepAlive = true;
} else {
const options: http.AgentOptions | https.AgentOptions = { keepAlive: true };
const agent = httpRequest.url.startsWith("https")
? new https.Agent(options)
: new http.Agent(options);
requestInit.agent = agent;
}
}
return requestInit;
}
async processRequest(operationResponse: HttpOperationResponse): Promise<void> {
if (this.cookieJar) {
const setCookieHeader = operationResponse.headers.get("Set-Cookie");
if (setCookieHeader != undefined) {
await new Promise((resolve, reject) => {
this.cookieJar!.setCookie(
setCookieHeader,
operationResponse.request.url,
{ ignoreError: true },
(err) => {
if (err) {
reject(err);
} else {
resolve();
}
}
);
});
}
}
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { RequestOptionsBase } from "./webResource";
/**
* A collection of properties that apply to a single invocation of an operation.
*/
export interface OperationArguments {
/**
* The parameters that were passed to the operation method.
*/
[parameterName: string]: any;
/**
* The optional arugments that are provided to an operation.
*/
options?: RequestOptionsBase;
}

View file

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { QueryCollectionFormat } from "./queryCollectionFormat";
import { Mapper } from "./serializer";
export type ParameterPath = string | string[] | { [propertyName: string]: ParameterPath };
/**
* A common interface that all Operation parameter's extend.
*/
export interface OperationParameter {
/**
* The path to this parameter's value in OperationArguments or the object that contains paths for
* each property's value in OperationArguments.
*/
parameterPath: ParameterPath;
/**
* The mapper that defines how to validate and serialize this parameter's value.
*/
mapper: Mapper;
}
/**
* A parameter for an operation that will be substituted into the operation's request URL.
*/
export interface OperationURLParameter extends OperationParameter {
/**
* Whether or not to skip encoding the URL parameter's value before adding it to the URL.
*/
skipEncoding?: boolean;
}
/**
* A parameter for an operation that will be added as a query parameter to the operation's HTTP
* request.
*/
export interface OperationQueryParameter extends OperationParameter {
/**
* Whether or not to skip encoding the query parameter's value before adding it to the URL.
*/
skipEncoding?: boolean;
/**
* If this query parameter's value is a collection, what type of format should the value be
* converted to.
*/
collectionFormat?: QueryCollectionFormat;
}
/**
* Get the path to this parameter's value as a dotted string (a.b.c).
* @param parameter The parameter to get the path string for.
* @returns The path to this parameter's value as a dotted string.
*/
export function getPathStringFromParameter(parameter: OperationParameter): string {
return getPathStringFromParameterPath(parameter.parameterPath, parameter.mapper);
}
export function getPathStringFromParameterPath(
parameterPath: ParameterPath,
mapper: Mapper
): string {
let result: string;
if (typeof parameterPath === "string") {
result = parameterPath;
} else if (Array.isArray(parameterPath)) {
result = parameterPath.join(".");
} else {
result = mapper.serializedName!;
}
return result;
}

View file

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { Mapper } from "./serializer";
/**
* An OperationResponse that can be returned from an operation request for a single status code.
*/
export interface OperationResponse {
/**
* The mapper that will be used to deserialize the response headers.
*/
headersMapper?: Mapper;
/**
* The mapper that will be used to deserialize the response body.
*/
bodyMapper?: Mapper;
}

98
node_modules/@azure/ms-rest-js/lib/operationSpec.ts generated vendored Normal file
View file

@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import {
OperationParameter,
OperationQueryParameter,
OperationURLParameter,
} from "./operationParameter";
import { OperationResponse } from "./operationResponse";
import { MapperType, Serializer } from "./serializer";
import { HttpMethods } from "./webResource";
/**
* A specification that defines an operation.
*/
export interface OperationSpec {
/**
* The serializer to use in this operation.
*/
readonly serializer: Serializer;
/**
* The HTTP method that should be used by requests for this operation.
*/
readonly httpMethod: HttpMethods;
/**
* The URL that was provided in the service's specification. This will still have all of the URL
* template variables in it. If this is not provided when the OperationSpec is created, then it
* will be populated by a "baseUri" property on the ServiceClient.
*/
readonly baseUrl?: string;
/**
* The fixed path for this operation's URL. This will still have all of the URL template variables
* in it.
*/
readonly path?: string;
/**
* The content type of the request body. This value will be used as the "Content-Type" header if
* it is provided.
*/
readonly contentType?: string;
/**
* The parameter that will be used to construct the HTTP request's body.
*/
readonly requestBody?: OperationParameter;
/**
* Whether or not this operation uses XML request and response bodies.
*/
readonly isXML?: boolean;
/**
* The parameters to the operation method that will be substituted into the constructed URL.
*/
readonly urlParameters?: ReadonlyArray<OperationURLParameter>;
/**
* The parameters to the operation method that will be added to the constructed URL's query.
*/
readonly queryParameters?: ReadonlyArray<OperationQueryParameter>;
/**
* The parameters to the operation method that will be converted to headers on the operation's
* HTTP request.
*/
readonly headerParameters?: ReadonlyArray<OperationParameter>;
/**
* The parameters to the operation method that will be used to create a formdata body for the
* operation's HTTP request.
*/
readonly formDataParameters?: ReadonlyArray<OperationParameter>;
/**
* The different types of responses that this operation can return based on what status code is
* returned.
*/
readonly responses: { [responseCode: string]: OperationResponse };
}
export function isStreamOperation(operationSpec: OperationSpec): boolean {
let result = false;
for (const statusCode in operationSpec.responses) {
const operationResponse: OperationResponse = operationSpec.responses[statusCode];
if (
operationResponse.bodyMapper &&
operationResponse.bodyMapper.type.name === MapperType.Stream
) {
result = true;
break;
}
}
return result;
}

View file

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { AgentSettings } from "../serviceClient";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
const agentNotSupportedInBrowser = new Error("AgentPolicy is not supported in browser environment");
export function agentPolicy(_agentSettings?: AgentSettings): RequestPolicyFactory {
return {
create: (_nextPolicy: RequestPolicy, _options: RequestPolicyOptionsLike) => {
throw agentNotSupportedInBrowser;
},
};
}
export class AgentPolicy extends BaseRequestPolicy {
constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) {
super(nextPolicy, options);
throw agentNotSupportedInBrowser;
}
public sendRequest(_request: WebResourceLike): Promise<HttpOperationResponse> {
throw agentNotSupportedInBrowser;
}
}

View file

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { AgentSettings } from "../serviceClient";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
export function agentPolicy(agentSettings?: AgentSettings): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new AgentPolicy(nextPolicy, options, agentSettings!);
},
};
}
export class AgentPolicy extends BaseRequestPolicy {
agentSettings: AgentSettings;
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
agentSettings: AgentSettings
) {
super(nextPolicy, options);
this.agentSettings = agentSettings;
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
if (!request.agentSettings) {
request.agentSettings = this.agentSettings;
}
return this._nextPolicy.sendRequest(request);
}
}

View file

@ -0,0 +1,294 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import { OperationResponse } from "../operationResponse";
import { OperationSpec, isStreamOperation } from "../operationSpec";
import { RestError } from "../restError";
import { Mapper, MapperType } from "../serializer";
import * as utils from "../util/utils";
import { parseXML } from "../util/xml";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
/**
* The content-types that will indicate that an operation response should be deserialized in a
* particular way.
*/
export interface DeserializationContentTypes {
/**
* The content-types that indicate that an operation response should be deserialized as JSON.
* Defaults to [ "application/json", "text/json" ].
*/
json?: string[];
/**
* The content-types that indicate that an operation response should be deserialized as XML.
* Defaults to [ "application/xml", "application/atom+xml" ].
*/
xml?: string[];
}
/**
* Create a new serialization RequestPolicyCreator that will serialized HTTP request bodies as they
* pass through the HTTP pipeline.
*/
export function deserializationPolicy(
deserializationContentTypes?: DeserializationContentTypes
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new DeserializationPolicy(nextPolicy, deserializationContentTypes, options);
},
};
}
export const defaultJsonContentTypes = ["application/json", "text/json"];
export const defaultXmlContentTypes = ["application/xml", "application/atom+xml"];
/**
* A RequestPolicy that will deserialize HTTP response bodies and headers as they pass through the
* HTTP pipeline.
*/
export class DeserializationPolicy extends BaseRequestPolicy {
public readonly jsonContentTypes: string[];
public readonly xmlContentTypes: string[];
constructor(
nextPolicy: RequestPolicy,
deserializationContentTypes: DeserializationContentTypes | undefined,
options: RequestPolicyOptionsLike
) {
super(nextPolicy, options);
this.jsonContentTypes =
(deserializationContentTypes && deserializationContentTypes.json) || defaultJsonContentTypes;
this.xmlContentTypes =
(deserializationContentTypes && deserializationContentTypes.xml) || defaultXmlContentTypes;
}
public async sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy
.sendRequest(request)
.then((response: HttpOperationResponse) =>
deserializeResponseBody(this.jsonContentTypes, this.xmlContentTypes, response)
);
}
}
function getOperationResponse(
parsedResponse: HttpOperationResponse
): undefined | OperationResponse {
let result: OperationResponse | undefined;
const request: WebResourceLike = parsedResponse.request;
const operationSpec: OperationSpec | undefined = request.operationSpec;
if (operationSpec) {
const operationResponseGetter:
| undefined
| ((
operationSpec: OperationSpec,
response: HttpOperationResponse
) => undefined | OperationResponse) = request.operationResponseGetter;
if (!operationResponseGetter) {
result = operationSpec.responses[parsedResponse.status];
} else {
result = operationResponseGetter(operationSpec, parsedResponse);
}
}
return result;
}
function shouldDeserializeResponse(parsedResponse: HttpOperationResponse): boolean {
const shouldDeserialize: undefined | boolean | ((response: HttpOperationResponse) => boolean) =
parsedResponse.request.shouldDeserialize;
let result: boolean;
if (shouldDeserialize === undefined) {
result = true;
} else if (typeof shouldDeserialize === "boolean") {
result = shouldDeserialize;
} else {
result = shouldDeserialize(parsedResponse);
}
return result;
}
export function deserializeResponseBody(
jsonContentTypes: string[],
xmlContentTypes: string[],
response: HttpOperationResponse
): Promise<HttpOperationResponse> {
return parse(jsonContentTypes, xmlContentTypes, response).then((parsedResponse) => {
const shouldDeserialize: boolean = shouldDeserializeResponse(parsedResponse);
if (shouldDeserialize) {
const operationSpec: OperationSpec | undefined = parsedResponse.request.operationSpec;
if (operationSpec && operationSpec.responses) {
const statusCode: number = parsedResponse.status;
const expectedStatusCodes: string[] = Object.keys(operationSpec.responses);
const hasNoExpectedStatusCodes: boolean =
expectedStatusCodes.length === 0 ||
(expectedStatusCodes.length === 1 && expectedStatusCodes[0] === "default");
const responseSpec: OperationResponse | undefined = getOperationResponse(parsedResponse);
const isExpectedStatusCode: boolean = hasNoExpectedStatusCodes
? 200 <= statusCode && statusCode < 300
: !!responseSpec;
if (!isExpectedStatusCode) {
const defaultResponseSpec: OperationResponse = operationSpec.responses.default;
if (defaultResponseSpec) {
const initialErrorMessage: string = isStreamOperation(operationSpec)
? `Unexpected status code: ${statusCode}`
: (parsedResponse.bodyAsText as string);
const error = new RestError(initialErrorMessage);
error.statusCode = statusCode;
error.request = utils.stripRequest(parsedResponse.request);
error.response = utils.stripResponse(parsedResponse);
let parsedErrorResponse: { [key: string]: any } = parsedResponse.parsedBody;
try {
if (parsedErrorResponse) {
const defaultResponseBodyMapper: Mapper | undefined =
defaultResponseSpec.bodyMapper;
if (
defaultResponseBodyMapper &&
defaultResponseBodyMapper.serializedName === "CloudError"
) {
if (parsedErrorResponse.error) {
parsedErrorResponse = parsedErrorResponse.error;
}
if (parsedErrorResponse.code) {
error.code = parsedErrorResponse.code;
}
if (parsedErrorResponse.message) {
error.message = parsedErrorResponse.message;
}
} else {
let internalError: any = parsedErrorResponse;
if (parsedErrorResponse.error) {
internalError = parsedErrorResponse.error;
}
error.code = internalError.code;
if (internalError.message) {
error.message = internalError.message;
}
}
if (defaultResponseBodyMapper) {
let valueToDeserialize: any = parsedErrorResponse;
if (
operationSpec.isXML &&
defaultResponseBodyMapper.type.name === MapperType.Sequence
) {
valueToDeserialize =
typeof parsedErrorResponse === "object"
? parsedErrorResponse[defaultResponseBodyMapper.xmlElementName!]
: [];
}
error.body = operationSpec.serializer.deserialize(
defaultResponseBodyMapper,
valueToDeserialize,
"error.body"
);
}
}
} catch (defaultError) {
error.message = `Error \"${defaultError.message}\" occurred in deserializing the responseBody - \"${parsedResponse.bodyAsText}\" for the default response.`;
}
return Promise.reject(error);
}
} else if (responseSpec) {
if (responseSpec.bodyMapper) {
let valueToDeserialize: any = parsedResponse.parsedBody;
if (operationSpec.isXML && responseSpec.bodyMapper.type.name === MapperType.Sequence) {
valueToDeserialize =
typeof valueToDeserialize === "object"
? valueToDeserialize[responseSpec.bodyMapper.xmlElementName!]
: [];
}
try {
parsedResponse.parsedBody = operationSpec.serializer.deserialize(
responseSpec.bodyMapper,
valueToDeserialize,
"operationRes.parsedBody"
);
} catch (error) {
const restError = new RestError(
`Error ${error} occurred in deserializing the responseBody - ${parsedResponse.bodyAsText}`
);
restError.request = utils.stripRequest(parsedResponse.request);
restError.response = utils.stripResponse(parsedResponse);
return Promise.reject(restError);
}
} else if (operationSpec.httpMethod === "HEAD") {
// head methods never have a body, but we return a boolean to indicate presence/absence of the resource
parsedResponse.parsedBody = response.status >= 200 && response.status < 300;
}
if (responseSpec.headersMapper) {
parsedResponse.parsedHeaders = operationSpec.serializer.deserialize(
responseSpec.headersMapper,
parsedResponse.headers.rawHeaders(),
"operationRes.parsedHeaders"
);
}
}
}
}
return Promise.resolve(parsedResponse);
});
}
function parse(
jsonContentTypes: string[],
xmlContentTypes: string[],
operationResponse: HttpOperationResponse
): Promise<HttpOperationResponse> {
const errorHandler = (err: Error & { code: string }) => {
const msg = `Error "${err}" occurred while parsing the response body - ${operationResponse.bodyAsText}.`;
const errCode = err.code || RestError.PARSE_ERROR;
const e = new RestError(
msg,
errCode,
operationResponse.status,
operationResponse.request,
operationResponse,
operationResponse.bodyAsText
);
return Promise.reject(e);
};
if (!operationResponse.request.streamResponseBody && operationResponse.bodyAsText) {
const text = operationResponse.bodyAsText;
const contentType: string = operationResponse.headers.get("Content-Type") || "";
const contentComponents: string[] = !contentType
? []
: contentType.split(";").map((component) => component.toLowerCase());
if (
contentComponents.length === 0 ||
contentComponents.some((component) => jsonContentTypes.indexOf(component) !== -1)
) {
return new Promise<HttpOperationResponse>((resolve) => {
operationResponse.parsedBody = JSON.parse(text);
resolve(operationResponse);
}).catch(errorHandler);
} else if (contentComponents.some((component) => xmlContentTypes.indexOf(component) !== -1)) {
return parseXML(text)
.then((body) => {
operationResponse.parsedBody = body;
return operationResponse;
})
.catch(errorHandler);
}
}
return Promise.resolve(operationResponse);
}

View file

@ -0,0 +1,220 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { RestError } from "../restError";
export interface RetryData {
retryCount: number;
retryInterval: number;
error?: RetryError;
}
export interface RetryError extends Error {
message: string;
code?: string;
innerError?: RetryError;
}
export function exponentialRetryPolicy(
retryCount?: number,
retryInterval?: number,
minRetryInterval?: number,
maxRetryInterval?: number
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new ExponentialRetryPolicy(
nextPolicy,
options,
retryCount,
retryInterval,
minRetryInterval,
maxRetryInterval
);
},
};
}
const DEFAULT_CLIENT_RETRY_INTERVAL = 1000 * 30;
const DEFAULT_CLIENT_RETRY_COUNT = 3;
const DEFAULT_CLIENT_MAX_RETRY_INTERVAL = 1000 * 90;
const DEFAULT_CLIENT_MIN_RETRY_INTERVAL = 1000 * 3;
/**
* @class
* Instantiates a new "ExponentialRetryPolicyFilter" instance.
*/
export class ExponentialRetryPolicy extends BaseRequestPolicy {
/**
* The client retry count.
*/
retryCount: number;
/**
* The client retry interval in milliseconds.
*/
retryInterval: number;
/**
* The minimum retry interval in milliseconds.
*/
minRetryInterval: number;
/**
* The maximum retry interval in milliseconds.
*/
maxRetryInterval: number;
/**
* @constructor
* @param {RequestPolicy} nextPolicy The next RequestPolicy in the pipeline chain.
* @param {RequestPolicyOptionsLike} options The options for this RequestPolicy.
* @param {number} [retryCount] The client retry count.
* @param {number} [retryInterval] The client retry interval, in milliseconds.
* @param {number} [minRetryInterval] The minimum retry interval, in milliseconds.
* @param {number} [maxRetryInterval] The maximum retry interval, in milliseconds.
*/
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
retryCount?: number,
retryInterval?: number,
minRetryInterval?: number,
maxRetryInterval?: number
) {
super(nextPolicy, options);
function isNumber(n: any): n is number {
return typeof n === "number";
}
this.retryCount = isNumber(retryCount) ? retryCount : DEFAULT_CLIENT_RETRY_COUNT;
this.retryInterval = isNumber(retryInterval) ? retryInterval : DEFAULT_CLIENT_RETRY_INTERVAL;
this.minRetryInterval = isNumber(minRetryInterval)
? minRetryInterval
: DEFAULT_CLIENT_MIN_RETRY_INTERVAL;
this.maxRetryInterval = isNumber(maxRetryInterval)
? maxRetryInterval
: DEFAULT_CLIENT_MAX_RETRY_INTERVAL;
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy
.sendRequest(request.clone())
.then((response) => retry(this, request, response))
.catch((error) => retry(this, request, error.response, undefined, error));
}
}
/**
* Determines if the operation should be retried and how long to wait until the next retry.
*
* @param {ExponentialRetryPolicy} policy The ExponentialRetryPolicy that this function is being called against.
* @param {number} statusCode The HTTP status code.
* @param {RetryData} retryData The retry data.
* @return {boolean} True if the operation qualifies for a retry; false otherwise.
*/
function shouldRetry(
policy: ExponentialRetryPolicy,
statusCode: number | undefined,
retryData: RetryData
): boolean {
if (
statusCode == undefined ||
(statusCode < 500 && statusCode !== 408) ||
statusCode === 501 ||
statusCode === 505
) {
return false;
}
let currentCount: number;
if (!retryData) {
throw new Error("retryData for the ExponentialRetryPolicyFilter cannot be null.");
} else {
currentCount = retryData && retryData.retryCount;
}
return currentCount < policy.retryCount;
}
/**
* Updates the retry data for the next attempt.
*
* @param {ExponentialRetryPolicy} policy The ExponentialRetryPolicy that this function is being called against.
* @param {RetryData} retryData The retry data.
* @param {RetryError} [err] The operation"s error, if any.
*/
function updateRetryData(
policy: ExponentialRetryPolicy,
retryData?: RetryData,
err?: RetryError
): RetryData {
if (!retryData) {
retryData = {
retryCount: 0,
retryInterval: 0,
};
}
if (err) {
if (retryData.error) {
err.innerError = retryData.error;
}
retryData.error = err;
}
// Adjust retry count
retryData.retryCount++;
// Adjust retry interval
let incrementDelta = Math.pow(2, retryData.retryCount) - 1;
const boundedRandDelta =
policy.retryInterval * 0.8 +
Math.floor(Math.random() * (policy.retryInterval * 1.2 - policy.retryInterval * 0.8));
incrementDelta *= boundedRandDelta;
retryData.retryInterval = Math.min(
policy.minRetryInterval + incrementDelta,
policy.maxRetryInterval
);
return retryData;
}
function retry(
policy: ExponentialRetryPolicy,
request: WebResourceLike,
response?: HttpOperationResponse,
retryData?: RetryData,
requestError?: RetryError
): Promise<HttpOperationResponse> {
retryData = updateRetryData(policy, retryData, requestError);
const isAborted: boolean | undefined = request.abortSignal && request.abortSignal.aborted;
if (!isAborted && shouldRetry(policy, response && response.status, retryData)) {
return utils
.delay(retryData.retryInterval)
.then(() => policy._nextPolicy.sendRequest(request.clone()))
.then((res) => retry(policy, request, res, retryData, undefined))
.catch((err) => retry(policy, request, response, retryData, err));
} else if (isAborted || requestError || !response) {
// If the operation failed in the end, return all errors instead of just the last one
const err =
retryData.error ||
new RestError(
"Failed to send the request.",
RestError.REQUEST_SEND_ERROR,
response && response.status,
response && response.request,
response
);
return Promise.reject(err);
} else {
return Promise.resolve(response);
}
}

View file

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function generateClientRequestIdPolicy(
requestIdHeaderName = "x-ms-client-request-id"
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new GenerateClientRequestIdPolicy(nextPolicy, options, requestIdHeaderName);
},
};
}
export class GenerateClientRequestIdPolicy extends BaseRequestPolicy {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
private _requestIdHeaderName: string
) {
super(nextPolicy, options);
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
if (!request.headers.contains(this._requestIdHeaderName)) {
request.headers.set(this._requestIdHeaderName, utils.generateUuid());
}
return this._nextPolicy.sendRequest(request);
}
}

View file

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function logPolicy(logger: any = console.log): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new LogPolicy(nextPolicy, options, logger);
},
};
}
export class LogPolicy extends BaseRequestPolicy {
logger?: any;
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
logger: any = console.log
) {
super(nextPolicy, options);
this.logger = logger;
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy.sendRequest(request).then((response) => logResponse(this, response));
}
}
function logResponse(
policy: LogPolicy,
response: HttpOperationResponse
): Promise<HttpOperationResponse> {
policy.logger(`>> Request: ${JSON.stringify(response.request, undefined, 2)}`);
policy.logger(`>> Response status code: ${response.status}`);
const responseBody = response.bodyAsText;
policy.logger(`>> Body: ${responseBody}`);
return Promise.resolve(response);
}

View file

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/*
* NOTE: When moving this file, please update "browser" section in package.json
* and "plugins" section in webpack.testconfig.ts.
*/
import { TelemetryInfo } from "./userAgentPolicy";
interface NavigatorEx extends Navigator {
// oscpu is not yet standards-compliant, but can not be undefined in TypeScript 3.6.2
readonly oscpu: string;
}
export function getDefaultUserAgentKey(): string {
return "x-ms-command-name";
}
export function getPlatformSpecificData(): TelemetryInfo[] {
const navigator = self.navigator as NavigatorEx;
const osInfo = {
key: "OS",
value: (navigator.oscpu || navigator.platform).replace(" ", ""),
};
return [osInfo];
}

View file

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as os from "os";
import { TelemetryInfo } from "./userAgentPolicy";
import { Constants } from "../util/constants";
export function getDefaultUserAgentKey(): string {
return Constants.HeaderConstants.USER_AGENT;
}
export function getPlatformSpecificData(): TelemetryInfo[] {
const runtimeInfo = {
key: "Node",
value: process.version,
};
const osInfo = {
key: "OS",
value: `(${os.arch()}-${os.type()}-${os.release()})`,
};
return [runtimeInfo, osInfo];
}

View file

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ProxySettings } from "../serviceClient";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
const proxyNotSupportedInBrowser = new Error("ProxyPolicy is not supported in browser environment");
export function getDefaultProxySettings(_proxyUrl?: string): ProxySettings | undefined {
return undefined;
}
export function proxyPolicy(_proxySettings?: ProxySettings): RequestPolicyFactory {
return {
create: (_nextPolicy: RequestPolicy, _options: RequestPolicyOptionsLike) => {
throw proxyNotSupportedInBrowser;
},
};
}
export class ProxyPolicy extends BaseRequestPolicy {
constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) {
super(nextPolicy, options);
throw proxyNotSupportedInBrowser;
}
public sendRequest(_request: WebResourceLike): Promise<HttpOperationResponse> {
throw proxyNotSupportedInBrowser;
}
}

View file

@ -0,0 +1,168 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { ProxySettings } from "../serviceClient";
import { WebResourceLike } from "../webResource";
import { Constants } from "../util/constants";
import { URLBuilder } from "../url";
/**
* @internal
*/
export const noProxyList: string[] = loadNoProxy();
const byPassedList: Map<string, boolean> = new Map();
/**
* @internal
*/
export function getEnvironmentValue(name: string): string | undefined {
if (process.env[name]) {
return process.env[name];
} else if (process.env[name.toLowerCase()]) {
return process.env[name.toLowerCase()];
}
return undefined;
}
function loadEnvironmentProxyValue(): string | undefined {
if (!process) {
return undefined;
}
const httpsProxy = getEnvironmentValue(Constants.HTTPS_PROXY);
const allProxy = getEnvironmentValue(Constants.ALL_PROXY);
const httpProxy = getEnvironmentValue(Constants.HTTP_PROXY);
return httpsProxy || allProxy || httpProxy;
}
// Check whether the host of a given `uri` is in the noProxyList.
// If there's a match, any request sent to the same host won't have the proxy settings set.
// This implementation is a port of https://github.com/Azure/azure-sdk-for-net/blob/8cca811371159e527159c7eb65602477898683e2/sdk/core/Azure.Core/src/Pipeline/Internal/HttpEnvironmentProxy.cs#L210
function isBypassed(uri: string): boolean | undefined {
if (noProxyList.length === 0) {
return false;
}
const host = URLBuilder.parse(uri).getHost()!;
if (byPassedList.has(host)) {
return byPassedList.get(host);
}
let isBypassedFlag = false;
for (const pattern of noProxyList) {
if (pattern[0] === ".") {
// This should match either domain it self or any subdomain or host
// .foo.com will match foo.com it self or *.foo.com
if (host.endsWith(pattern)) {
isBypassedFlag = true;
} else {
if (host.length === pattern.length - 1 && host === pattern.slice(1)) {
isBypassedFlag = true;
}
}
} else {
if (host === pattern) {
isBypassedFlag = true;
}
}
}
byPassedList.set(host, isBypassedFlag);
return isBypassedFlag;
}
/**
* @internal
*/
export function loadNoProxy(): string[] {
const noProxy = getEnvironmentValue(Constants.NO_PROXY);
if (noProxy) {
return noProxy
.split(",")
.map((item) => item.trim())
.filter((item) => item.length);
}
return [];
}
/**
* @internal
*/
function extractAuthFromUrl(
url: string
): { username?: string; password?: string; urlWithoutAuth: string } {
const atIndex = url.indexOf("@");
if (atIndex === -1) {
return { urlWithoutAuth: url };
}
const schemeIndex = url.indexOf("://");
const authStart = schemeIndex !== -1 ? schemeIndex + 3 : 0;
const auth = url.substring(authStart, atIndex);
const colonIndex = auth.indexOf(":");
const hasPassword = colonIndex !== -1;
const username = hasPassword ? auth.substring(0, colonIndex) : auth;
const password = hasPassword ? auth.substring(colonIndex + 1) : undefined;
const urlWithoutAuth = url.substring(0, authStart) + url.substring(atIndex + 1);
return {
username,
password,
urlWithoutAuth,
};
}
export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined {
if (!proxyUrl) {
proxyUrl = loadEnvironmentProxyValue();
if (!proxyUrl) {
return undefined;
}
}
const { username, password, urlWithoutAuth } = extractAuthFromUrl(proxyUrl);
const parsedUrl = URLBuilder.parse(urlWithoutAuth);
const schema = parsedUrl.getScheme() ? parsedUrl.getScheme() + "://" : "";
return {
host: schema + parsedUrl.getHost(),
port: Number.parseInt(parsedUrl.getPort() || "80"),
username,
password,
};
}
export function proxyPolicy(proxySettings?: ProxySettings): RequestPolicyFactory {
if (!proxySettings) {
proxySettings = getDefaultProxySettings();
}
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new ProxyPolicy(nextPolicy, options, proxySettings!);
},
};
}
export class ProxyPolicy extends BaseRequestPolicy {
proxySettings: ProxySettings;
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
proxySettings: ProxySettings
) {
super(nextPolicy, options);
this.proxySettings = proxySettings;
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
if (!request.proxySettings && !isBypassed(request.url)) {
request.proxySettings = this.proxySettings;
}
return this._nextPolicy.sendRequest(request);
}
}

View file

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import { URLBuilder } from "../url";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
/**
* Options for how redirect responses are handled.
*/
export interface RedirectOptions {
/*
* When true, redirect responses are followed. Defaults to true.
*/
handleRedirects: boolean;
/*
* The maximum number of times the redirect URL will be tried before
* failing. Defaults to 20.
*/
maxRetries?: number;
}
export const DefaultRedirectOptions: RedirectOptions = {
handleRedirects: true,
maxRetries: 20,
};
export function redirectPolicy(maximumRetries = 20): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new RedirectPolicy(nextPolicy, options, maximumRetries);
},
};
}
export class RedirectPolicy extends BaseRequestPolicy {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
readonly maxRetries = 20
) {
super(nextPolicy, options);
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy
.sendRequest(request)
.then((response) => handleRedirect(this, response, 0));
}
}
function handleRedirect(
policy: RedirectPolicy,
response: HttpOperationResponse,
currentRetries: number
): Promise<HttpOperationResponse> {
const { request, status } = response;
const locationHeader = response.headers.get("location");
if (
locationHeader &&
(status === 300 ||
(status === 301 && ["GET", "HEAD"].includes(request.method)) ||
(status === 302 && ["GET", "POST", "HEAD"].includes(request.method)) ||
(status === 303 && "POST" === request.method) ||
status === 307) &&
((request.redirectLimit !== undefined && currentRetries < request.redirectLimit) ||
(request.redirectLimit === undefined && currentRetries < policy.maxRetries))
) {
const builder = URLBuilder.parse(request.url);
builder.setPath(locationHeader);
request.url = builder.toString();
// POST request with Status code 302 and 303 should be converted into a
// redirected GET request if the redirect url is present in the location header
// reference: https://tools.ietf.org/html/rfc7231#page-57 && https://fetch.spec.whatwg.org/#http-redirect-fetch
if ((status === 302 || status === 303) && request.method === "POST") {
request.method = "GET";
delete request.body;
}
return policy._nextPolicy
.sendRequest(request)
.then((res) => handleRedirect(policy, res, currentRetries + 1))
.then((res) => recordRedirect(res, request.url));
}
return Promise.resolve(response);
}
function recordRedirect(response: HttpOperationResponse, redirect: string): HttpOperationResponse {
// This is called as the recursive calls to handleRedirect() unwind,
// only record the deepest/last redirect
if (!response.redirected) {
response.redirected = true;
response.url = redirect;
}
return response;
}

View file

@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import { HttpPipelineLogger } from "../httpPipelineLogger";
import { HttpPipelineLogLevel } from "../httpPipelineLogLevel";
import { WebResourceLike } from "../webResource";
/**
* Creates a new RequestPolicy per-request that uses the provided nextPolicy.
*/
export type RequestPolicyFactory = {
create(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike): RequestPolicy;
};
export interface RequestPolicy {
sendRequest(httpRequest: WebResourceLike): Promise<HttpOperationResponse>;
}
export abstract class BaseRequestPolicy implements RequestPolicy {
protected constructor(
readonly _nextPolicy: RequestPolicy,
readonly _options: RequestPolicyOptionsLike
) {}
public abstract sendRequest(webResource: WebResourceLike): Promise<HttpOperationResponse>;
/**
* Get whether or not a log with the provided log level should be logged.
* @param logLevel The log level of the log that will be logged.
* @returns Whether or not a log with the provided log level should be logged.
*/
public shouldLog(logLevel: HttpPipelineLogLevel): boolean {
return this._options.shouldLog(logLevel);
}
/**
* Attempt to log the provided message to the provided logger. If no logger was provided or if
* the log level does not meat the logger's threshold, then nothing will be logged.
* @param logLevel The log level of this log.
* @param message The message of this log.
*/
public log(logLevel: HttpPipelineLogLevel, message: string): void {
this._options.log(logLevel, message);
}
}
/**
* Optional properties that can be used when creating a RequestPolicy.
*/
export interface RequestPolicyOptionsLike {
/**
* Get whether or not a log with the provided log level should be logged.
* @param logLevel The log level of the log that will be logged.
* @returns Whether or not a log with the provided log level should be logged.
*/
shouldLog(logLevel: HttpPipelineLogLevel): boolean;
/**
* Attempt to log the provided message to the provided logger. If no logger was provided or if
* the log level does not meet the logger's threshold, then nothing will be logged.
* @param logLevel The log level of this log.
* @param message The message of this log.
*/
log(logLevel: HttpPipelineLogLevel, message: string): void;
}
/**
* Optional properties that can be used when creating a RequestPolicy.
*/
export class RequestPolicyOptions implements RequestPolicyOptionsLike {
constructor(private _logger?: HttpPipelineLogger) {}
/**
* Get whether or not a log with the provided log level should be logged.
* @param logLevel The log level of the log that will be logged.
* @returns Whether or not a log with the provided log level should be logged.
*/
public shouldLog(logLevel: HttpPipelineLogLevel): boolean {
return (
!!this._logger &&
logLevel !== HttpPipelineLogLevel.OFF &&
logLevel <= this._logger.minimumLogLevel
);
}
/**
* Attempt to log the provided message to the provided logger. If no logger was provided or if
* the log level does not meat the logger's threshold, then nothing will be logged.
* @param logLevel The log level of this log.
* @param message The message of this log.
*/
public log(logLevel: HttpPipelineLogLevel, message: string): void {
if (this._logger && this.shouldLog(logLevel)) {
this._logger.log(logLevel, message);
}
}
}

View file

@ -0,0 +1,197 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function rpRegistrationPolicy(retryTimeout = 30): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new RPRegistrationPolicy(nextPolicy, options, retryTimeout);
},
};
}
export class RPRegistrationPolicy extends BaseRequestPolicy {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
readonly _retryTimeout = 30
) {
super(nextPolicy, options);
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy
.sendRequest(request.clone())
.then((response) => registerIfNeeded(this, request, response));
}
}
function registerIfNeeded(
policy: RPRegistrationPolicy,
request: WebResourceLike,
response: HttpOperationResponse
): Promise<HttpOperationResponse> {
if (response.status === 409) {
const rpName = checkRPNotRegisteredError(response.bodyAsText as string);
if (rpName) {
const urlPrefix = extractSubscriptionUrl(request.url);
return (
registerRP(policy, urlPrefix, rpName, request)
// Autoregistration of ${provider} failed for some reason. We will not return this error
// instead will return the initial response with 409 status code back to the user.
// do nothing here as we are returning the original response at the end of this method.
.catch(() => false)
.then((registrationStatus) => {
if (registrationStatus) {
// Retry the original request. We have to change the x-ms-client-request-id
// otherwise Azure endpoint will return the initial 409 (cached) response.
request.headers.set("x-ms-client-request-id", utils.generateUuid());
return policy._nextPolicy.sendRequest(request.clone());
}
return response;
})
);
}
}
return Promise.resolve(response);
}
/**
* Reuses the headers of the original request and url (if specified).
* @param {WebResourceLike} originalRequest The original request
* @param {boolean} reuseUrlToo Should the url from the original request be reused as well. Default false.
* @returns {object} A new request object with desired headers.
*/
function getRequestEssentials(
originalRequest: WebResourceLike,
reuseUrlToo = false
): WebResourceLike {
const reqOptions: WebResourceLike = originalRequest.clone();
if (reuseUrlToo) {
reqOptions.url = originalRequest.url;
}
// We have to change the x-ms-client-request-id otherwise Azure endpoint
// will return the initial 409 (cached) response.
reqOptions.headers.set("x-ms-client-request-id", utils.generateUuid());
// Set content-type to application/json
reqOptions.headers.set("Content-Type", "application/json; charset=utf-8");
return reqOptions;
}
/**
* Validates the error code and message associated with 409 response status code. If it matches to that of
* RP not registered then it returns the name of the RP else returns undefined.
* @param {string} body The response body received after making the original request.
* @returns {string} The name of the RP if condition is satisfied else undefined.
*/
function checkRPNotRegisteredError(body: string): string {
let result, responseBody;
if (body) {
try {
responseBody = JSON.parse(body);
} catch (err) {
// do nothing;
}
if (
responseBody &&
responseBody.error &&
responseBody.error.message &&
responseBody.error.code &&
responseBody.error.code === "MissingSubscriptionRegistration"
) {
const matchRes = responseBody.error.message.match(/.*'(.*)'/i);
if (matchRes) {
result = matchRes.pop();
}
}
}
return result;
}
/**
* Extracts the first part of the URL, just after subscription:
* https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/
* @param {string} url The original request url
* @returns {string} The url prefix as explained above.
*/
function extractSubscriptionUrl(url: string): string {
let result;
const matchRes = url.match(/.*\/subscriptions\/[a-f0-9-]+\//gi);
if (matchRes && matchRes[0]) {
result = matchRes[0];
} else {
throw new Error(`Unable to extract subscriptionId from the given url - ${url}.`);
}
return result;
}
/**
* Registers the given provider.
* @param {RPRegistrationPolicy} policy The RPRegistrationPolicy this function is being called against.
* @param {string} urlPrefix https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/
* @param {string} provider The provider name to be registered.
* @param {WebResourceLike} originalRequest The original request sent by the user that returned a 409 response
* with a message that the provider is not registered.
* @param {registrationCallback} callback The callback that handles the RP registration
*/
function registerRP(
policy: RPRegistrationPolicy,
urlPrefix: string,
provider: string,
originalRequest: WebResourceLike
): Promise<boolean> {
const postUrl = `${urlPrefix}providers/${provider}/register?api-version=2016-02-01`;
const getUrl = `${urlPrefix}providers/${provider}?api-version=2016-02-01`;
const reqOptions = getRequestEssentials(originalRequest);
reqOptions.method = "POST";
reqOptions.url = postUrl;
return policy._nextPolicy.sendRequest(reqOptions).then((response) => {
if (response.status !== 200) {
throw new Error(`Autoregistration of ${provider} failed. Please try registering manually.`);
}
return getRegistrationStatus(policy, getUrl, originalRequest);
});
}
/**
* Polls the registration status of the provider that was registered. Polling happens at an interval of 30 seconds.
* Polling will happen till the registrationState property of the response body is "Registered".
* @param {RPRegistrationPolicy} policy The RPRegistrationPolicy this function is being called against.
* @param {string} url The request url for polling
* @param {WebResourceLike} originalRequest The original request sent by the user that returned a 409 response
* with a message that the provider is not registered.
* @returns {Promise<boolean>} True if RP Registration is successful.
*/
function getRegistrationStatus(
policy: RPRegistrationPolicy,
url: string,
originalRequest: WebResourceLike
): Promise<boolean> {
const reqOptions: any = getRequestEssentials(originalRequest);
reqOptions.url = url;
reqOptions.method = "GET";
return policy._nextPolicy.sendRequest(reqOptions).then((res) => {
const obj = res.parsedBody as any;
if (res.parsedBody && obj.registrationState && obj.registrationState === "Registered") {
return true;
} else {
return utils
.delay(policy._retryTimeout * 1000)
.then(() => getRegistrationStatus(policy, url, originalRequest));
}
});
}

View file

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ServiceClientCredentials } from "../credentials/serviceClientCredentials";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicyFactory,
RequestPolicy,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function signingPolicy(
authenticationProvider: ServiceClientCredentials
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new SigningPolicy(nextPolicy, options, authenticationProvider);
},
};
}
export class SigningPolicy extends BaseRequestPolicy {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
public authenticationProvider: ServiceClientCredentials
) {
super(nextPolicy, options);
}
signRequest(request: WebResourceLike): Promise<WebResourceLike> {
return this.authenticationProvider.signRequest(request);
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this.signRequest(request).then((nextRequest) =>
this._nextPolicy.sendRequest(nextRequest)
);
}
}

View file

@ -0,0 +1,187 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export interface RetryData {
retryCount: number;
retryInterval: number;
error?: RetryError;
}
export interface RetryError extends Error {
message: string;
code?: string;
innerError?: RetryError;
}
export function systemErrorRetryPolicy(
retryCount?: number,
retryInterval?: number,
minRetryInterval?: number,
maxRetryInterval?: number
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new SystemErrorRetryPolicy(
nextPolicy,
options,
retryCount,
retryInterval,
minRetryInterval,
maxRetryInterval
);
},
};
}
/**
* @class
* Instantiates a new "ExponentialRetryPolicyFilter" instance.
*
* @constructor
* @param {number} retryCount The client retry count.
* @param {number} retryInterval The client retry interval, in milliseconds.
* @param {number} minRetryInterval The minimum retry interval, in milliseconds.
* @param {number} maxRetryInterval The maximum retry interval, in milliseconds.
*/
export class SystemErrorRetryPolicy extends BaseRequestPolicy {
retryCount: number;
retryInterval: number;
minRetryInterval: number;
maxRetryInterval: number;
DEFAULT_CLIENT_RETRY_INTERVAL = 1000 * 30;
DEFAULT_CLIENT_RETRY_COUNT = 3;
DEFAULT_CLIENT_MAX_RETRY_INTERVAL = 1000 * 90;
DEFAULT_CLIENT_MIN_RETRY_INTERVAL = 1000 * 3;
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
retryCount?: number,
retryInterval?: number,
minRetryInterval?: number,
maxRetryInterval?: number
) {
super(nextPolicy, options);
this.retryCount = typeof retryCount === "number" ? retryCount : this.DEFAULT_CLIENT_RETRY_COUNT;
this.retryInterval =
typeof retryInterval === "number" ? retryInterval : this.DEFAULT_CLIENT_RETRY_INTERVAL;
this.minRetryInterval =
typeof minRetryInterval === "number"
? minRetryInterval
: this.DEFAULT_CLIENT_MIN_RETRY_INTERVAL;
this.maxRetryInterval =
typeof maxRetryInterval === "number"
? maxRetryInterval
: this.DEFAULT_CLIENT_MAX_RETRY_INTERVAL;
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy
.sendRequest(request.clone())
.catch((error) => retry(this, request, error.response, error));
}
}
/**
* Determines if the operation should be retried and how long to wait until the next retry.
*
* @param {number} statusCode The HTTP status code.
* @param {RetryData} retryData The retry data.
* @return {boolean} True if the operation qualifies for a retry; false otherwise.
*/
function shouldRetry(policy: SystemErrorRetryPolicy, retryData: RetryData): boolean {
let currentCount;
if (!retryData) {
throw new Error("retryData for the SystemErrorRetryPolicyFilter cannot be null.");
} else {
currentCount = retryData && retryData.retryCount;
}
return currentCount < policy.retryCount;
}
/**
* Updates the retry data for the next attempt.
*
* @param {RetryData} retryData The retry data.
* @param {object} err The operation"s error, if any.
*/
function updateRetryData(
policy: SystemErrorRetryPolicy,
retryData?: RetryData,
err?: RetryError
): RetryData {
if (!retryData) {
retryData = {
retryCount: 0,
retryInterval: 0,
};
}
if (err) {
if (retryData.error) {
err.innerError = retryData.error;
}
retryData.error = err;
}
// Adjust retry count
retryData.retryCount++;
// Adjust retry interval
let incrementDelta = Math.pow(2, retryData.retryCount) - 1;
const boundedRandDelta =
policy.retryInterval * 0.8 + Math.floor(Math.random() * (policy.retryInterval * 0.4));
incrementDelta *= boundedRandDelta;
retryData.retryInterval = Math.min(
policy.minRetryInterval + incrementDelta,
policy.maxRetryInterval
);
return retryData;
}
async function retry(
policy: SystemErrorRetryPolicy,
request: WebResourceLike,
operationResponse: HttpOperationResponse,
err?: RetryError,
retryData?: RetryData
): Promise<HttpOperationResponse> {
retryData = updateRetryData(policy, retryData, err);
if (
err &&
err.code &&
shouldRetry(policy, retryData) &&
(err.code === "ETIMEDOUT" ||
err.code === "ESOCKETTIMEDOUT" ||
err.code === "ECONNREFUSED" ||
err.code === "ECONNRESET" ||
err.code === "ENOENT")
) {
// If previous operation ended with an error and the policy allows a retry, do that
try {
await utils.delay(retryData.retryInterval);
return policy._nextPolicy.sendRequest(request.clone());
} catch (error) {
return retry(policy, request, operationResponse, error, retryData);
}
} else {
if (err) {
// If the operation failed in the end, return all errors instead of just the last one
return Promise.reject(retryData.error);
}
return operationResponse;
}
}

View file

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyOptionsLike,
RequestPolicyFactory,
} from "./requestPolicy";
import { WebResourceLike } from "../webResource";
import { HttpOperationResponse } from "../httpOperationResponse";
import { Constants } from "../util/constants";
import { delay } from "../util/utils";
const StatusCodes = Constants.HttpConstants.StatusCodes;
const DEFAULT_RETRY_COUNT = 3;
/**
* Options that control how to retry on response status code 429.
*/
export interface ThrottlingRetryOptions {
/**
* The maximum number of retry attempts. Defaults to 3.
*/
maxRetries?: number;
}
export function throttlingRetryPolicy(
maxRetries: number = DEFAULT_RETRY_COUNT
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new ThrottlingRetryPolicy(nextPolicy, options, maxRetries);
},
};
}
/**
* To learn more, please refer to
* https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-request-limits,
* https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits and
* https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/troubleshooting-throttling-errors
*/
export class ThrottlingRetryPolicy extends BaseRequestPolicy {
private retryLimit: number;
constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike, retryLimit: number) {
super(nextPolicy, options);
this.retryLimit = retryLimit;
}
public async sendRequest(httpRequest: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy.sendRequest(httpRequest.clone()).then((response) => {
return this.retry(httpRequest, response, 0);
});
}
private async retry(
httpRequest: WebResourceLike,
httpResponse: HttpOperationResponse,
retryCount: number
): Promise<HttpOperationResponse> {
if (httpResponse.status !== StatusCodes.TooManyRequests) {
return httpResponse;
}
const retryAfterHeader: string | undefined = httpResponse.headers.get(
Constants.HeaderConstants.RETRY_AFTER
);
if (retryAfterHeader && retryCount < this.retryLimit) {
const delayInMs: number | undefined = ThrottlingRetryPolicy.parseRetryAfterHeader(
retryAfterHeader
);
if (delayInMs) {
await delay(delayInMs);
const res = await this._nextPolicy.sendRequest(httpRequest);
return this.retry(httpRequest, res, retryCount + 1);
}
}
return httpResponse;
}
public static parseRetryAfterHeader(headerValue: string): number | undefined {
const retryAfterInSeconds = Number(headerValue);
if (Number.isNaN(retryAfterInSeconds)) {
return ThrottlingRetryPolicy.parseDateRetryAfterHeader(headerValue);
} else {
return retryAfterInSeconds * 1000;
}
}
public static parseDateRetryAfterHeader(headerValue: string): number | undefined {
try {
const now: number = Date.now();
const date: number = Date.parse(headerValue);
const diff = date - now;
return Number.isNaN(diff) ? undefined : diff;
} catch (error) {
return undefined;
}
}
}

View file

@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpHeaders } from "../httpHeaders";
import { HttpOperationResponse } from "../httpOperationResponse";
import { Constants } from "../util/constants";
import { WebResourceLike } from "../webResource";
import { getDefaultUserAgentKey, getPlatformSpecificData } from "./msRestUserAgentPolicy";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export type TelemetryInfo = { key?: string; value?: string };
function getRuntimeInfo(): TelemetryInfo[] {
const msRestRuntime = {
key: "ms-rest-js",
value: Constants.msRestVersion,
};
return [msRestRuntime];
}
function getUserAgentString(
telemetryInfo: TelemetryInfo[],
keySeparator = " ",
valueSeparator = "/"
): string {
return telemetryInfo
.map((info) => {
const value = info.value ? `${valueSeparator}${info.value}` : "";
return `${info.key}${value}`;
})
.join(keySeparator);
}
export const getDefaultUserAgentHeaderName = getDefaultUserAgentKey;
export function getDefaultUserAgentValue(): string {
const runtimeInfo = getRuntimeInfo();
const platformSpecificData = getPlatformSpecificData();
const userAgent = getUserAgentString(runtimeInfo.concat(platformSpecificData));
return userAgent;
}
export function userAgentPolicy(userAgentData?: TelemetryInfo): RequestPolicyFactory {
const key: string =
!userAgentData || userAgentData.key == undefined ? getDefaultUserAgentKey() : userAgentData.key;
const value: string =
!userAgentData || userAgentData.value == undefined
? getDefaultUserAgentValue()
: userAgentData.value;
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new UserAgentPolicy(nextPolicy, options, key, value);
},
};
}
export class UserAgentPolicy extends BaseRequestPolicy {
constructor(
readonly _nextPolicy: RequestPolicy,
readonly _options: RequestPolicyOptionsLike,
protected headerKey: string,
protected headerValue: string
) {
super(_nextPolicy, _options);
}
sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
this.addUserAgentHeader(request);
return this._nextPolicy.sendRequest(request);
}
addUserAgentHeader(request: WebResourceLike): void {
if (!request.headers) {
request.headers = new HttpHeaders();
}
if (!request.headers.get(this.headerKey) && this.headerValue) {
request.headers.set(this.headerKey, this.headerValue);
}
}
}

81
node_modules/@azure/ms-rest-js/lib/proxyAgent.ts generated vendored Normal file
View file

@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as http from "http";
import * as https from "https";
import * as tunnel from "tunnel";
import { ProxySettings } from "./serviceClient";
import { URLBuilder } from "./url";
import { HttpHeadersLike } from "./httpHeaders";
export type ProxyAgent = { isHttps: boolean; agent: http.Agent | https.Agent };
export function createProxyAgent(
requestUrl: string,
proxySettings: ProxySettings,
headers?: HttpHeadersLike
): ProxyAgent {
const tunnelOptions: tunnel.HttpsOverHttpsOptions = {
proxy: {
host: URLBuilder.parse(proxySettings.host).getHost() as string,
port: proxySettings.port,
headers: (headers && headers.rawHeaders()) || {},
},
};
if (proxySettings.username && proxySettings.password) {
tunnelOptions.proxy!.proxyAuth = `${proxySettings.username}:${proxySettings.password}`;
} else if (proxySettings.username) {
tunnelOptions.proxy!.proxyAuth = `${proxySettings.username}`;
}
const requestScheme = URLBuilder.parse(requestUrl).getScheme() || "";
const isRequestHttps = requestScheme.toLowerCase() === "https";
const proxyScheme = URLBuilder.parse(proxySettings.host).getScheme() || "";
const isProxyHttps = proxyScheme.toLowerCase() === "https";
const proxyAgent = {
isHttps: isRequestHttps,
agent: createTunnel(isRequestHttps, isProxyHttps, tunnelOptions),
};
return proxyAgent;
}
// Duplicate tunnel.HttpsOverHttpsOptions to avoid exporting createTunnel() with dependency on @types/tunnel
// createIunnel() is only imported by tests.
export interface HttpsProxyOptions {
host: string;
port: number;
localAddress?: string;
proxyAuth?: string;
headers?: { [key: string]: any };
ca?: Buffer[];
servername?: string;
key?: Buffer;
cert?: Buffer;
}
interface HttpsOverHttpsOptions {
maxSockets?: number;
ca?: Buffer[];
key?: Buffer;
cert?: Buffer;
proxy?: HttpsProxyOptions;
}
export function createTunnel(
isRequestHttps: boolean,
isProxyHttps: boolean,
tunnelOptions: HttpsOverHttpsOptions
): http.Agent | https.Agent {
if (isRequestHttps && isProxyHttps) {
return tunnel.httpsOverHttps(tunnelOptions);
} else if (isRequestHttps && !isProxyHttps) {
return tunnel.httpsOverHttp(tunnelOptions);
} else if (!isRequestHttps && isProxyHttps) {
return tunnel.httpOverHttps(tunnelOptions);
} else {
return tunnel.httpOverHttp(tunnelOptions);
}
}

View file

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* The format that will be used to join an array of values together for a query parameter value.
*/
export enum QueryCollectionFormat {
Csv = ",",
Ssv = " ",
Tsv = "\t",
Pipes = "|",
Multi = "Multi",
}

34
node_modules/@azure/ms-rest-js/lib/restError.ts generated vendored Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResourceLike } from "./webResource";
export class RestError extends Error {
static readonly REQUEST_SEND_ERROR: string = "REQUEST_SEND_ERROR";
static readonly REQUEST_ABORTED_ERROR: string = "REQUEST_ABORTED_ERROR";
static readonly PARSE_ERROR: string = "PARSE_ERROR";
code?: string;
statusCode?: number;
request?: WebResourceLike;
response?: HttpOperationResponse;
body?: any;
constructor(
message: string,
code?: string,
statusCode?: number,
request?: WebResourceLike,
response?: HttpOperationResponse,
body?: any
) {
super(message);
this.code = code;
this.statusCode = statusCode;
this.request = request;
this.response = response;
this.body = body;
Object.setPrototypeOf(this, RestError.prototype);
}
}

1063
node_modules/@azure/ms-rest-js/lib/serializer.ts generated vendored Normal file

File diff suppressed because it is too large Load diff

852
node_modules/@azure/ms-rest-js/lib/serviceClient.ts generated vendored Normal file
View file

@ -0,0 +1,852 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
import { ServiceClientCredentials } from "./credentials/serviceClientCredentials";
import { DefaultHttpClient } from "./defaultHttpClient";
import { HttpClient } from "./httpClient";
import { HttpOperationResponse, RestResponse } from "./httpOperationResponse";
import { HttpPipelineLogger } from "./httpPipelineLogger";
import { OperationArguments } from "./operationArguments";
import {
getPathStringFromParameter,
getPathStringFromParameterPath,
OperationParameter,
ParameterPath,
} from "./operationParameter";
import { isStreamOperation, OperationSpec } from "./operationSpec";
import {
deserializationPolicy,
DeserializationContentTypes,
} from "./policies/deserializationPolicy";
import { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
import { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
import {
userAgentPolicy,
getDefaultUserAgentHeaderName,
getDefaultUserAgentValue,
} from "./policies/userAgentPolicy";
import { DefaultRedirectOptions, RedirectOptions, redirectPolicy } from "./policies/redirectPolicy";
import {
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptions,
RequestPolicyOptionsLike,
} from "./policies/requestPolicy";
import { rpRegistrationPolicy } from "./policies/rpRegistrationPolicy";
import { signingPolicy } from "./policies/signingPolicy";
import { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
import { QueryCollectionFormat } from "./queryCollectionFormat";
import { CompositeMapper, DictionaryMapper, Mapper, MapperType, Serializer } from "./serializer";
import { URLBuilder } from "./url";
import * as utils from "./util/utils";
import { stringifyXML } from "./util/xml";
import {
RequestOptionsBase,
RequestPrepareOptions,
WebResourceLike,
isWebResourceLike,
WebResource,
} from "./webResource";
import { OperationResponse } from "./operationResponse";
import { ServiceCallback } from "./util/utils";
import { agentPolicy } from "./policies/agentPolicy";
import { proxyPolicy, getDefaultProxySettings } from "./policies/proxyPolicy";
import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy";
import { Agent } from "http";
import {
AzureIdentityCredentialAdapter,
azureResourceManagerEndpoints,
} from "./credentials/azureIdentityTokenCredentialAdapter";
/**
* HTTP proxy settings (Node.js only)
*/
export interface ProxySettings {
host: string;
port: number;
username?: string;
password?: string;
}
/**
* HTTP and HTTPS agents (Node.js only)
*/
export interface AgentSettings {
http: Agent;
https: Agent;
}
/**
* Options to be provided while creating the client.
*/
export interface ServiceClientOptions {
/**
* An array of factories which get called to create the RequestPolicy pipeline used to send a HTTP
* request on the wire, or a function that takes in the defaultRequestPolicyFactories and returns
* the requestPolicyFactories that will be used.
*/
requestPolicyFactories?:
| RequestPolicyFactory[]
| ((defaultRequestPolicyFactories: RequestPolicyFactory[]) => void | RequestPolicyFactory[]);
/**
* The HttpClient that will be used to send HTTP requests.
*/
httpClient?: HttpClient;
/**
* The HttpPipelineLogger that can be used to debug RequestPolicies within the HTTP pipeline.
*/
httpPipelineLogger?: HttpPipelineLogger;
/**
* If set to true, turn off the default retry policy.
*/
noRetryPolicy?: boolean;
/**
* Gets or sets the retry timeout in seconds for AutomaticRPRegistration. Default value is 30.
*/
rpRegistrationRetryTimeout?: number;
/**
* Whether or not to generate a client request ID header for each HTTP request.
*/
generateClientRequestIdHeader?: boolean;
/**
* Whether to include credentials in CORS requests in the browser.
* See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials for more information.
*/
withCredentials?: boolean;
/**
* If specified, a GenerateRequestIdPolicy will be added to the HTTP pipeline that will add a
* header to all outgoing requests with this header name and a random UUID as the request ID.
*/
clientRequestIdHeaderName?: string;
/**
* The content-types that will be associated with JSON or XML serialization.
*/
deserializationContentTypes?: DeserializationContentTypes;
/**
* The header name to use for the telemetry header while sending the request. If this is not
* specified, then "User-Agent" will be used when running on Node.js and "x-ms-command-name" will
* be used when running in a browser.
*/
userAgentHeaderName?: string | ((defaultUserAgentHeaderName: string) => string);
/**
* The string to be set to the telemetry header while sending the request, or a function that
* takes in the default user-agent string and returns the user-agent string that will be used.
*/
userAgent?: string | ((defaultUserAgent: string) => string);
/**
* Proxy settings which will be used for every HTTP request (Node.js only).
*/
proxySettings?: ProxySettings;
/**
* Options for how redirect responses are handled.
*/
redirectOptions?: RedirectOptions;
/**
* HTTP and HTTPS agents which will be used for every HTTP request (Node.js only).
*/
agentSettings?: AgentSettings;
/**
* If specified:
* - This `baseUri` becomes the base URI that requests will be made against for this ServiceClient.
* - 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".
*
* If it is not specified:
* - All OperationSpecs must contain a baseUrl property.
* - If a `TokenCredential` was passed through the constructor, the `getToken` scope is set to be "https://management.azure.com/.default".
*/
baseUri?: string;
}
/**
* @class
* Initializes a new instance of the ServiceClient.
*/
export class ServiceClient {
/**
* The base URI against which requests will be made when using this ServiceClient instance.
*
* This can be set either by setting the `baseUri` in the `options` parameter to the ServiceClient constructor or directly after constructing the ServiceClient.
* 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"
*
* If it is not specified, all OperationSpecs must contain a baseUrl property.
*/
protected baseUri?: string;
/**
* The default request content type for the service.
* Used if no requestContentType is present on an OperationSpec.
*/
protected requestContentType?: string;
/**
* The HTTP client that will be used to send requests.
*/
private readonly _httpClient: HttpClient;
private readonly _requestPolicyOptions: RequestPolicyOptionsLike;
private readonly _requestPolicyFactories: RequestPolicyFactory[];
private readonly _withCredentials: boolean;
/**
* The ServiceClient constructor
* @constructor
* @param {ServiceClientCredentials} [credentials] The credentials object used for authentication.
* @param {ServiceClientOptions} [options] The service client options that govern the behavior of the client.
*/
constructor(
credentials?: ServiceClientCredentials | TokenCredential,
options?: ServiceClientOptions
) {
if (!options) {
options = {};
}
if (options.baseUri) {
this.baseUri = options.baseUri;
}
let serviceClientCredentials: ServiceClientCredentials | undefined;
if (isTokenCredential(credentials)) {
let scope: string | undefined = undefined;
if (options?.baseUri && azureResourceManagerEndpoints.includes(options?.baseUri)) {
scope = `${options.baseUri}/.default`;
}
serviceClientCredentials = new AzureIdentityCredentialAdapter(credentials, scope);
} else {
serviceClientCredentials = credentials;
}
if (serviceClientCredentials && !serviceClientCredentials.signRequest) {
throw new Error("credentials argument needs to implement signRequest method");
}
this._withCredentials = options.withCredentials || false;
this._httpClient = options.httpClient || new DefaultHttpClient();
this._requestPolicyOptions = new RequestPolicyOptions(options.httpPipelineLogger);
let requestPolicyFactories: RequestPolicyFactory[];
if (Array.isArray(options.requestPolicyFactories)) {
requestPolicyFactories = options.requestPolicyFactories;
} else {
requestPolicyFactories = createDefaultRequestPolicyFactories(
serviceClientCredentials,
options
);
if (options.requestPolicyFactories) {
const newRequestPolicyFactories:
| void
| RequestPolicyFactory[] = options.requestPolicyFactories(requestPolicyFactories);
if (newRequestPolicyFactories) {
requestPolicyFactories = newRequestPolicyFactories;
}
}
}
this._requestPolicyFactories = requestPolicyFactories;
}
/**
* Send the provided httpRequest.
*/
sendRequest(options: RequestPrepareOptions | WebResourceLike): Promise<HttpOperationResponse> {
if (options === null || options === undefined || typeof options !== "object") {
throw new Error("options cannot be null or undefined and it must be of type object.");
}
let httpRequest: WebResourceLike;
try {
if (isWebResourceLike(options)) {
options.validateRequestProperties();
httpRequest = options;
} else {
httpRequest = new WebResource();
httpRequest = httpRequest.prepare(options);
}
} catch (error) {
return Promise.reject(error);
}
let httpPipeline: RequestPolicy = this._httpClient;
if (this._requestPolicyFactories && this._requestPolicyFactories.length > 0) {
for (let i = this._requestPolicyFactories.length - 1; i >= 0; --i) {
httpPipeline = this._requestPolicyFactories[i].create(
httpPipeline,
this._requestPolicyOptions
);
}
}
return httpPipeline.sendRequest(httpRequest);
}
/**
* Send an HTTP request that is populated using the provided OperationSpec.
* @param {OperationArguments} operationArguments The arguments that the HTTP request's templated values will be populated from.
* @param {OperationSpec} operationSpec The OperationSpec to use to populate the httpRequest.
* @param {ServiceCallback} callback The callback to call when the response is received.
*/
sendOperationRequest(
operationArguments: OperationArguments,
operationSpec: OperationSpec,
callback?: ServiceCallback<any>
): Promise<RestResponse> {
if (typeof operationArguments.options === "function") {
callback = operationArguments.options;
operationArguments.options = undefined;
}
const httpRequest = new WebResource();
let result: Promise<RestResponse>;
try {
const baseUri: string | undefined = operationSpec.baseUrl || this.baseUri;
if (!baseUri) {
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."
);
}
httpRequest.method = operationSpec.httpMethod;
httpRequest.operationSpec = operationSpec;
const requestUrl: URLBuilder = URLBuilder.parse(baseUri);
if (operationSpec.path) {
requestUrl.appendPath(operationSpec.path);
}
if (operationSpec.urlParameters && operationSpec.urlParameters.length > 0) {
for (const urlParameter of operationSpec.urlParameters) {
let urlParameterValue: string = getOperationArgumentValueFromParameter(
this,
operationArguments,
urlParameter,
operationSpec.serializer
);
urlParameterValue = operationSpec.serializer.serialize(
urlParameter.mapper,
urlParameterValue,
getPathStringFromParameter(urlParameter)
);
if (!urlParameter.skipEncoding) {
urlParameterValue = encodeURIComponent(urlParameterValue);
}
requestUrl.replaceAll(
`{${urlParameter.mapper.serializedName || getPathStringFromParameter(urlParameter)}}`,
urlParameterValue
);
}
}
if (operationSpec.queryParameters && operationSpec.queryParameters.length > 0) {
for (const queryParameter of operationSpec.queryParameters) {
let queryParameterValue: any = getOperationArgumentValueFromParameter(
this,
operationArguments,
queryParameter,
operationSpec.serializer
);
if (queryParameterValue != undefined) {
queryParameterValue = operationSpec.serializer.serialize(
queryParameter.mapper,
queryParameterValue,
getPathStringFromParameter(queryParameter)
);
if (queryParameter.collectionFormat != undefined) {
if (queryParameter.collectionFormat === QueryCollectionFormat.Multi) {
if (queryParameterValue.length === 0) {
queryParameterValue = "";
} else {
for (const index in queryParameterValue) {
const item = queryParameterValue[index];
queryParameterValue[index] = item == undefined ? "" : item.toString();
}
}
} else if (
queryParameter.collectionFormat === QueryCollectionFormat.Ssv ||
queryParameter.collectionFormat === QueryCollectionFormat.Tsv
) {
queryParameterValue = queryParameterValue.join(queryParameter.collectionFormat);
}
}
if (!queryParameter.skipEncoding) {
if (Array.isArray(queryParameterValue)) {
for (const index in queryParameterValue) {
if (
queryParameterValue[index] !== undefined &&
queryParameterValue[index] !== null
) {
queryParameterValue[index] = encodeURIComponent(queryParameterValue[index]);
}
}
} else {
queryParameterValue = encodeURIComponent(queryParameterValue);
}
}
if (
queryParameter.collectionFormat != undefined &&
queryParameter.collectionFormat !== QueryCollectionFormat.Multi &&
queryParameter.collectionFormat !== QueryCollectionFormat.Ssv &&
queryParameter.collectionFormat !== QueryCollectionFormat.Tsv
) {
queryParameterValue = queryParameterValue.join(queryParameter.collectionFormat);
}
requestUrl.setQueryParameter(
queryParameter.mapper.serializedName || getPathStringFromParameter(queryParameter),
queryParameterValue
);
}
}
}
httpRequest.url = requestUrl.toString();
const contentType = operationSpec.contentType || this.requestContentType;
if (contentType) {
httpRequest.headers.set("Content-Type", contentType);
}
if (operationSpec.headerParameters) {
for (const headerParameter of operationSpec.headerParameters) {
let headerValue: any = getOperationArgumentValueFromParameter(
this,
operationArguments,
headerParameter,
operationSpec.serializer
);
if (headerValue != undefined) {
headerValue = operationSpec.serializer.serialize(
headerParameter.mapper,
headerValue,
getPathStringFromParameter(headerParameter)
);
const headerCollectionPrefix = (headerParameter.mapper as DictionaryMapper)
.headerCollectionPrefix;
if (headerCollectionPrefix) {
for (const key of Object.keys(headerValue)) {
httpRequest.headers.set(headerCollectionPrefix + key, headerValue[key]);
}
} else {
httpRequest.headers.set(
headerParameter.mapper.serializedName ||
getPathStringFromParameter(headerParameter),
headerValue
);
}
}
}
}
const options: RequestOptionsBase | undefined = operationArguments.options;
if (options) {
if (options.customHeaders) {
for (const customHeaderName in options.customHeaders) {
httpRequest.headers.set(customHeaderName, options.customHeaders[customHeaderName]);
}
}
if (options.abortSignal) {
httpRequest.abortSignal = options.abortSignal;
}
if (options.timeout) {
httpRequest.timeout = options.timeout;
}
if (options.onUploadProgress) {
httpRequest.onUploadProgress = options.onUploadProgress;
}
if (options.onDownloadProgress) {
httpRequest.onDownloadProgress = options.onDownloadProgress;
}
}
httpRequest.withCredentials = this._withCredentials;
serializeRequestBody(this, httpRequest, operationArguments, operationSpec);
if (httpRequest.streamResponseBody == undefined) {
httpRequest.streamResponseBody = isStreamOperation(operationSpec);
}
result = this.sendRequest(httpRequest).then((res) =>
flattenResponse(res, operationSpec.responses[res.status])
);
} catch (error) {
result = Promise.reject(error);
}
const cb = callback;
if (cb) {
result
// tslint:disable-next-line:no-null-keyword
.then((res) => cb(null, res._response.parsedBody, res._response.request, res._response))
.catch((err) => cb(err));
}
return result;
}
}
export function serializeRequestBody(
serviceClient: ServiceClient,
httpRequest: WebResourceLike,
operationArguments: OperationArguments,
operationSpec: OperationSpec
): void {
if (operationSpec.requestBody && operationSpec.requestBody.mapper) {
httpRequest.body = getOperationArgumentValueFromParameter(
serviceClient,
operationArguments,
operationSpec.requestBody,
operationSpec.serializer
);
const bodyMapper = operationSpec.requestBody.mapper;
const { required, xmlName, xmlElementName, serializedName } = bodyMapper;
const typeName = bodyMapper.type.name;
try {
if (httpRequest.body != undefined || required) {
const requestBodyParameterPathString: string = getPathStringFromParameter(
operationSpec.requestBody
);
httpRequest.body = operationSpec.serializer.serialize(
bodyMapper,
httpRequest.body,
requestBodyParameterPathString
);
const isStream = typeName === MapperType.Stream;
if (operationSpec.isXML) {
if (typeName === MapperType.Sequence) {
httpRequest.body = stringifyXML(
utils.prepareXMLRootList(
httpRequest.body,
xmlElementName || xmlName || serializedName!
),
{ rootName: xmlName || serializedName }
);
} else if (!isStream) {
httpRequest.body = stringifyXML(httpRequest.body, {
rootName: xmlName || serializedName,
});
}
} else if (!isStream) {
httpRequest.body = JSON.stringify(httpRequest.body);
}
}
} catch (error) {
throw new Error(
`Error "${error.message}" occurred in serializing the payload - ${JSON.stringify(
serializedName,
undefined,
" "
)}.`
);
}
} else if (operationSpec.formDataParameters && operationSpec.formDataParameters.length > 0) {
httpRequest.formData = {};
for (const formDataParameter of operationSpec.formDataParameters) {
const formDataParameterValue: any = getOperationArgumentValueFromParameter(
serviceClient,
operationArguments,
formDataParameter,
operationSpec.serializer
);
if (formDataParameterValue != undefined) {
const formDataParameterPropertyName: string =
formDataParameter.mapper.serializedName || getPathStringFromParameter(formDataParameter);
httpRequest.formData[formDataParameterPropertyName] = operationSpec.serializer.serialize(
formDataParameter.mapper,
formDataParameterValue,
getPathStringFromParameter(formDataParameter)
);
}
}
}
}
function isRequestPolicyFactory(instance: any): instance is RequestPolicyFactory {
return typeof instance.create === "function";
}
function getValueOrFunctionResult(
value: undefined | string | ((defaultValue: string) => string),
defaultValueCreator: () => string
): string {
let result: string;
if (typeof value === "string") {
result = value;
} else {
result = defaultValueCreator();
if (typeof value === "function") {
result = value(result);
}
}
return result;
}
function createDefaultRequestPolicyFactories(
credentials: ServiceClientCredentials | RequestPolicyFactory | undefined,
options: ServiceClientOptions
): RequestPolicyFactory[] {
const factories: RequestPolicyFactory[] = [];
if (options.generateClientRequestIdHeader) {
factories.push(generateClientRequestIdPolicy(options.clientRequestIdHeaderName));
}
if (credentials) {
if (isRequestPolicyFactory(credentials)) {
factories.push(credentials);
} else {
factories.push(signingPolicy(credentials));
}
}
const userAgentHeaderName: string = getValueOrFunctionResult(
options.userAgentHeaderName,
getDefaultUserAgentHeaderName
);
const userAgentHeaderValue: string = getValueOrFunctionResult(
options.userAgent,
getDefaultUserAgentValue
);
if (userAgentHeaderName && userAgentHeaderValue) {
factories.push(userAgentPolicy({ key: userAgentHeaderName, value: userAgentHeaderValue }));
}
const redirectOptions = {
...DefaultRedirectOptions,
...options.redirectOptions,
};
if (redirectOptions.handleRedirects) {
factories.push(redirectPolicy(redirectOptions.maxRetries));
}
factories.push(rpRegistrationPolicy(options.rpRegistrationRetryTimeout));
if (!options.noRetryPolicy) {
factories.push(exponentialRetryPolicy());
factories.push(systemErrorRetryPolicy());
factories.push(throttlingRetryPolicy());
}
factories.push(deserializationPolicy(options.deserializationContentTypes));
const proxySettings = options.proxySettings || getDefaultProxySettings();
if (proxySettings) {
factories.push(proxyPolicy(proxySettings));
}
if (options.agentSettings) {
factories.push(agentPolicy(options.agentSettings));
}
return factories;
}
export type PropertyParent = { [propertyName: string]: any };
/**
* Get the property parent for the property at the provided path when starting with the provided
* parent object.
*/
export function getPropertyParent(parent: PropertyParent, propertyPath: string[]): PropertyParent {
if (parent && propertyPath) {
const propertyPathLength: number = propertyPath.length;
for (let i = 0; i < propertyPathLength - 1; ++i) {
const propertyName: string = propertyPath[i];
if (!parent[propertyName]) {
parent[propertyName] = {};
}
parent = parent[propertyName];
}
}
return parent;
}
function getOperationArgumentValueFromParameter(
serviceClient: ServiceClient,
operationArguments: OperationArguments,
parameter: OperationParameter,
serializer: Serializer
): any {
return getOperationArgumentValueFromParameterPath(
serviceClient,
operationArguments,
parameter.parameterPath,
parameter.mapper,
serializer
);
}
export function getOperationArgumentValueFromParameterPath(
serviceClient: ServiceClient,
operationArguments: OperationArguments,
parameterPath: ParameterPath,
parameterMapper: Mapper,
serializer: Serializer
): any {
let value: any;
if (typeof parameterPath === "string") {
parameterPath = [parameterPath];
}
if (Array.isArray(parameterPath)) {
if (parameterPath.length > 0) {
if (parameterMapper.isConstant) {
value = parameterMapper.defaultValue;
} else {
let propertySearchResult: PropertySearchResult = getPropertyFromParameterPath(
operationArguments,
parameterPath
);
if (!propertySearchResult.propertyFound) {
propertySearchResult = getPropertyFromParameterPath(serviceClient, parameterPath);
}
let useDefaultValue = false;
if (!propertySearchResult.propertyFound) {
useDefaultValue =
parameterMapper.required ||
(parameterPath[0] === "options" && parameterPath.length === 2);
}
value = useDefaultValue ? parameterMapper.defaultValue : propertySearchResult.propertyValue;
}
// Serialize just for validation purposes.
const parameterPathString: string = getPathStringFromParameterPath(
parameterPath,
parameterMapper
);
serializer.serialize(parameterMapper, value, parameterPathString);
}
} else {
if (parameterMapper.required) {
value = {};
}
for (const propertyName in parameterPath) {
const propertyMapper: Mapper = (parameterMapper as CompositeMapper).type.modelProperties![
propertyName
];
const propertyPath: ParameterPath = parameterPath[propertyName];
const propertyValue: any = getOperationArgumentValueFromParameterPath(
serviceClient,
operationArguments,
propertyPath,
propertyMapper,
serializer
);
// Serialize just for validation purposes.
const propertyPathString: string = getPathStringFromParameterPath(
propertyPath,
propertyMapper
);
serializer.serialize(propertyMapper, propertyValue, propertyPathString);
if (propertyValue !== undefined) {
if (!value) {
value = {};
}
value[propertyName] = propertyValue;
}
}
}
return value;
}
interface PropertySearchResult {
propertyValue?: any;
propertyFound: boolean;
}
function getPropertyFromParameterPath(
parent: { [parameterName: string]: any },
parameterPath: string[]
): PropertySearchResult {
const result: PropertySearchResult = { propertyFound: false };
let i = 0;
for (; i < parameterPath.length; ++i) {
const parameterPathPart: string = parameterPath[i];
// Make sure to check inherited properties too, so don't use hasOwnProperty().
if (parent != undefined && parameterPathPart in parent) {
parent = parent[parameterPathPart];
} else {
break;
}
}
if (i === parameterPath.length) {
result.propertyValue = parent;
result.propertyFound = true;
}
return result;
}
export function flattenResponse(
_response: HttpOperationResponse,
responseSpec: OperationResponse | undefined
): RestResponse {
const parsedHeaders = _response.parsedHeaders;
const bodyMapper = responseSpec && responseSpec.bodyMapper;
const addOperationResponse = (obj: {}) =>
Object.defineProperty(obj, "_response", {
value: _response,
});
if (bodyMapper) {
const typeName = bodyMapper.type.name;
if (typeName === "Stream") {
return addOperationResponse({
...parsedHeaders,
blobBody: _response.blobBody,
readableStreamBody: _response.readableStreamBody,
});
}
const modelProperties =
(typeName === "Composite" && (bodyMapper as CompositeMapper).type.modelProperties) || {};
const isPageableResponse = Object.keys(modelProperties).some(
(k) => modelProperties[k].serializedName === ""
);
if (typeName === "Sequence" || isPageableResponse) {
// We're expecting a sequece(array) make sure that the response body is in the
// correct format, if not make it an empty array []
const parsedBody = Array.isArray(_response.parsedBody) ? _response.parsedBody : [];
const arrayResponse = [...parsedBody] as RestResponse & any[];
for (const key of Object.keys(modelProperties)) {
if (modelProperties[key].serializedName) {
arrayResponse[key] = _response.parsedBody[key];
}
}
if (parsedHeaders) {
for (const key of Object.keys(parsedHeaders)) {
arrayResponse[key] = parsedHeaders[key];
}
}
addOperationResponse(arrayResponse);
return arrayResponse;
}
if (typeName === "Composite" || typeName === "Dictionary") {
return addOperationResponse({
...parsedHeaders,
..._response.parsedBody,
});
}
}
if (
bodyMapper ||
_response.request.method === "HEAD" ||
utils.isPrimitiveType(_response.parsedBody)
) {
// primitive body types and HEAD booleans
return addOperationResponse({
...parsedHeaders,
body: _response.parsedBody,
});
}
return addOperationResponse({
...parsedHeaders,
..._response.parsedBody,
});
}

659
node_modules/@azure/ms-rest-js/lib/url.ts generated vendored Normal file
View file

@ -0,0 +1,659 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { replaceAll } from "./util/utils";
type URLQueryParseState = "ParameterName" | "ParameterValue" | "Invalid";
/**
* A class that handles the query portion of a URLBuilder.
*/
export class URLQuery {
private readonly _rawQuery: { [queryParameterName: string]: string | string[] } = {};
/**
* Get whether or not there any query parameters in this URLQuery.
*/
public any(): boolean {
return Object.keys(this._rawQuery).length > 0;
}
/**
* Set a query parameter with the provided name and value. If the parameterValue is undefined or
* empty, then this will attempt to remove an existing query parameter with the provided
* parameterName.
*/
public set(parameterName: string, parameterValue: any): void {
if (parameterName) {
if (parameterValue != undefined) {
const newValue = Array.isArray(parameterValue) ? parameterValue : parameterValue.toString();
this._rawQuery[parameterName] = newValue;
} else {
delete this._rawQuery[parameterName];
}
}
}
/**
* Get the value of the query parameter with the provided name. If no parameter exists with the
* provided parameter name, then undefined will be returned.
*/
public get(parameterName: string): string | string[] | undefined {
return parameterName ? this._rawQuery[parameterName] : undefined;
}
/**
* Get the string representation of this query. The return value will not start with a "?".
*/
public toString(): string {
let result = "";
for (const parameterName in this._rawQuery) {
if (result) {
result += "&";
}
const parameterValue = this._rawQuery[parameterName];
if (Array.isArray(parameterValue)) {
const parameterStrings = [];
for (const parameterValueElement of parameterValue) {
parameterStrings.push(`${parameterName}=${parameterValueElement}`);
}
result += parameterStrings.join("&");
} else {
result += `${parameterName}=${parameterValue}`;
}
}
return result;
}
/**
* Parse a URLQuery from the provided text.
*/
public static parse(text: string): URLQuery {
const result = new URLQuery();
if (text) {
if (text.startsWith("?")) {
text = text.substring(1);
}
let currentState: URLQueryParseState = "ParameterName";
let parameterName = "";
let parameterValue = "";
for (let i = 0; i < text.length; ++i) {
const currentCharacter: string = text[i];
switch (currentState) {
case "ParameterName":
switch (currentCharacter) {
case "=":
currentState = "ParameterValue";
break;
case "&":
parameterName = "";
parameterValue = "";
break;
default:
parameterName += currentCharacter;
break;
}
break;
case "ParameterValue":
switch (currentCharacter) {
case "&":
result.set(parameterName, parameterValue);
parameterName = "";
parameterValue = "";
currentState = "ParameterName";
break;
default:
parameterValue += currentCharacter;
break;
}
break;
default:
throw new Error("Unrecognized URLQuery parse state: " + currentState);
}
}
if (currentState === "ParameterValue") {
result.set(parameterName, parameterValue);
}
}
return result;
}
}
/**
* A class that handles creating, modifying, and parsing URLs.
*/
export class URLBuilder {
private _scheme: string | undefined;
private _host: string | undefined;
private _port: string | undefined;
private _path: string | undefined;
private _query: URLQuery | undefined;
/**
* Set the scheme/protocol for this URL. If the provided scheme contains other parts of a URL
* (such as a host, port, path, or query), those parts will be added to this URL as well.
*/
public setScheme(scheme: string | undefined): void {
if (!scheme) {
this._scheme = undefined;
} else {
this.set(scheme, "SCHEME");
}
}
/**
* Get the scheme that has been set in this URL.
*/
public getScheme(): string | undefined {
return this._scheme;
}
/**
* Set the host for this URL. If the provided host contains other parts of a URL (such as a
* port, path, or query), those parts will be added to this URL as well.
*/
public setHost(host: string | undefined): void {
if (!host) {
this._host = undefined;
} else {
this.set(host, "SCHEME_OR_HOST");
}
}
/**
* Get the host that has been set in this URL.
*/
public getHost(): string | undefined {
return this._host;
}
/**
* Set the port for this URL. If the provided port contains other parts of a URL (such as a
* path or query), those parts will be added to this URL as well.
*/
public setPort(port: number | string | undefined): void {
if (port == undefined || port === "") {
this._port = undefined;
} else {
this.set(port.toString(), "PORT");
}
}
/**
* Get the port that has been set in this URL.
*/
public getPort(): string | undefined {
return this._port;
}
/**
* Set the path for this URL. If the provided path contains a query, then it will be added to
* this URL as well.
*/
public setPath(path: string | undefined): void {
if (!path) {
this._path = undefined;
} else {
const schemeIndex = path.indexOf("://");
if (schemeIndex !== -1) {
const schemeStart = path.lastIndexOf("/", schemeIndex);
// Make sure to only grab the URL part of the path before setting the state back to SCHEME
// this will handle cases such as "/a/b/c/https://microsoft.com" => "https://microsoft.com"
this.set(schemeStart === -1 ? path : path.substr(schemeStart + 1), "SCHEME");
} else {
this.set(path, "PATH");
}
}
}
/**
* Append the provided path to this URL's existing path. If the provided path contains a query,
* then it will be added to this URL as well.
*/
public appendPath(path: string | undefined): void {
if (path) {
let currentPath: string | undefined = this.getPath();
if (currentPath) {
if (!currentPath.endsWith("/")) {
currentPath += "/";
}
if (path.startsWith("/")) {
path = path.substring(1);
}
path = currentPath + path;
}
this.set(path, "PATH");
}
}
/**
* Get the path that has been set in this URL.
*/
public getPath(): string | undefined {
return this._path;
}
/**
* Set the query in this URL.
*/
public setQuery(query: string | undefined): void {
if (!query) {
this._query = undefined;
} else {
this._query = URLQuery.parse(query);
}
}
/**
* Set a query parameter with the provided name and value in this URL's query. If the provided
* query parameter value is undefined or empty, then the query parameter will be removed if it
* existed.
*/
public setQueryParameter(queryParameterName: string, queryParameterValue: any): void {
if (queryParameterName) {
if (!this._query) {
this._query = new URLQuery();
}
this._query.set(queryParameterName, queryParameterValue);
}
}
/**
* Get the value of the query parameter with the provided query parameter name. If no query
* parameter exists with the provided name, then undefined will be returned.
*/
public getQueryParameterValue(queryParameterName: string): string | string[] | undefined {
return this._query ? this._query.get(queryParameterName) : undefined;
}
/**
* Get the query in this URL.
*/
public getQuery(): string | undefined {
return this._query ? this._query.toString() : undefined;
}
/**
* Set the parts of this URL by parsing the provided text using the provided startState.
*/
private set(text: string, startState: URLTokenizerState): void {
const tokenizer = new URLTokenizer(text, startState);
while (tokenizer.next()) {
const token: URLToken | undefined = tokenizer.current();
if (token) {
switch (token.type) {
case "SCHEME":
this._scheme = token.text || undefined;
break;
case "HOST":
this._host = token.text || undefined;
break;
case "PORT":
this._port = token.text || undefined;
break;
case "PATH":
const tokenPath: string | undefined = token.text || undefined;
if (!this._path || this._path === "/" || tokenPath !== "/") {
this._path = tokenPath;
}
break;
case "QUERY":
this._query = URLQuery.parse(token.text);
break;
default:
throw new Error(`Unrecognized URLTokenType: ${token.type}`);
}
}
}
}
public toString(): string {
let result = "";
if (this._scheme) {
result += `${this._scheme}://`;
}
if (this._host) {
result += this._host;
}
if (this._port) {
result += `:${this._port}`;
}
if (this._path) {
if (!this._path.startsWith("/")) {
result += "/";
}
result += this._path;
}
if (this._query && this._query.any()) {
result += `?${this._query.toString()}`;
}
return result;
}
/**
* If the provided searchValue is found in this URLBuilder, then replace it with the provided
* replaceValue.
*/
public replaceAll(searchValue: string, replaceValue: string): void {
if (searchValue) {
this.setScheme(replaceAll(this.getScheme(), searchValue, replaceValue));
this.setHost(replaceAll(this.getHost(), searchValue, replaceValue));
this.setPort(replaceAll(this.getPort(), searchValue, replaceValue));
this.setPath(replaceAll(this.getPath(), searchValue, replaceValue));
this.setQuery(replaceAll(this.getQuery(), searchValue, replaceValue));
}
}
public static parse(text: string): URLBuilder {
const result = new URLBuilder();
result.set(text, "SCHEME_OR_HOST");
return result;
}
}
type URLTokenizerState = "SCHEME" | "SCHEME_OR_HOST" | "HOST" | "PORT" | "PATH" | "QUERY" | "DONE";
type URLTokenType = "SCHEME" | "HOST" | "PORT" | "PATH" | "QUERY";
export class URLToken {
public constructor(public readonly text: string, public readonly type: URLTokenType) {}
public static scheme(text: string): URLToken {
return new URLToken(text, "SCHEME");
}
public static host(text: string): URLToken {
return new URLToken(text, "HOST");
}
public static port(text: string): URLToken {
return new URLToken(text, "PORT");
}
public static path(text: string): URLToken {
return new URLToken(text, "PATH");
}
public static query(text: string): URLToken {
return new URLToken(text, "QUERY");
}
}
/**
* Get whether or not the provided character (single character string) is an alphanumeric (letter or
* digit) character.
*/
export function isAlphaNumericCharacter(character: string): boolean {
const characterCode: number = character.charCodeAt(0);
return (
(48 /* '0' */ <= characterCode && characterCode <= 57) /* '9' */ ||
(65 /* 'A' */ <= characterCode && characterCode <= 90) /* 'Z' */ ||
(97 /* 'a' */ <= characterCode && characterCode <= 122) /* 'z' */
);
}
/**
* A class that tokenizes URL strings.
*/
export class URLTokenizer {
readonly _textLength: number;
_currentState: URLTokenizerState;
_currentIndex: number;
_currentToken: URLToken | undefined;
public constructor(readonly _text: string, state?: URLTokenizerState) {
this._textLength = _text ? _text.length : 0;
this._currentState = state != undefined ? state : "SCHEME_OR_HOST";
this._currentIndex = 0;
}
/**
* Get the current URLToken this URLTokenizer is pointing at, or undefined if the URLTokenizer
* hasn't started or has finished tokenizing.
*/
public current(): URLToken | undefined {
return this._currentToken;
}
/**
* Advance to the next URLToken and return whether or not a URLToken was found.
*/
public next(): boolean {
if (!hasCurrentCharacter(this)) {
this._currentToken = undefined;
} else {
switch (this._currentState) {
case "SCHEME":
nextScheme(this);
break;
case "SCHEME_OR_HOST":
nextSchemeOrHost(this);
break;
case "HOST":
nextHost(this);
break;
case "PORT":
nextPort(this);
break;
case "PATH":
nextPath(this);
break;
case "QUERY":
nextQuery(this);
break;
default:
throw new Error(`Unrecognized URLTokenizerState: ${this._currentState}`);
}
}
return !!this._currentToken;
}
}
/**
* Read the remaining characters from this Tokenizer's character stream.
*/
function readRemaining(tokenizer: URLTokenizer): string {
let result = "";
if (tokenizer._currentIndex < tokenizer._textLength) {
result = tokenizer._text.substring(tokenizer._currentIndex);
tokenizer._currentIndex = tokenizer._textLength;
}
return result;
}
/**
* Whether or not this URLTokenizer has a current character.
*/
function hasCurrentCharacter(tokenizer: URLTokenizer): boolean {
return tokenizer._currentIndex < tokenizer._textLength;
}
/**
* Get the character in the text string at the current index.
*/
function getCurrentCharacter(tokenizer: URLTokenizer): string {
return tokenizer._text[tokenizer._currentIndex];
}
/**
* Advance to the character in text that is "step" characters ahead. If no step value is provided,
* then step will default to 1.
*/
function nextCharacter(tokenizer: URLTokenizer, step?: number): void {
if (hasCurrentCharacter(tokenizer)) {
if (!step) {
step = 1;
}
tokenizer._currentIndex += step;
}
}
/**
* Starting with the current character, peek "charactersToPeek" number of characters ahead in this
* Tokenizer's stream of characters.
*/
function peekCharacters(tokenizer: URLTokenizer, charactersToPeek: number): string {
let endIndex: number = tokenizer._currentIndex + charactersToPeek;
if (tokenizer._textLength < endIndex) {
endIndex = tokenizer._textLength;
}
return tokenizer._text.substring(tokenizer._currentIndex, endIndex);
}
/**
* Read characters from this Tokenizer until the end of the stream or until the provided condition
* is false when provided the current character.
*/
function readWhile(tokenizer: URLTokenizer, condition: (character: string) => boolean): string {
let result = "";
while (hasCurrentCharacter(tokenizer)) {
const currentCharacter: string = getCurrentCharacter(tokenizer);
if (!condition(currentCharacter)) {
break;
} else {
result += currentCharacter;
nextCharacter(tokenizer);
}
}
return result;
}
/**
* Read characters from this Tokenizer until a non-alphanumeric character or the end of the
* character stream is reached.
*/
function readWhileLetterOrDigit(tokenizer: URLTokenizer): string {
return readWhile(tokenizer, (character: string) => isAlphaNumericCharacter(character));
}
/**
* Read characters from this Tokenizer until one of the provided terminating characters is read or
* the end of the character stream is reached.
*/
function readUntilCharacter(tokenizer: URLTokenizer, ...terminatingCharacters: string[]): string {
return readWhile(
tokenizer,
(character: string) => terminatingCharacters.indexOf(character) === -1
);
}
function nextScheme(tokenizer: URLTokenizer): void {
const scheme: string = readWhileLetterOrDigit(tokenizer);
tokenizer._currentToken = URLToken.scheme(scheme);
if (!hasCurrentCharacter(tokenizer)) {
tokenizer._currentState = "DONE";
} else {
tokenizer._currentState = "HOST";
}
}
function nextSchemeOrHost(tokenizer: URLTokenizer): void {
const schemeOrHost: string = readUntilCharacter(tokenizer, ":", "/", "?");
if (!hasCurrentCharacter(tokenizer)) {
tokenizer._currentToken = URLToken.host(schemeOrHost);
tokenizer._currentState = "DONE";
} else if (getCurrentCharacter(tokenizer) === ":") {
if (peekCharacters(tokenizer, 3) === "://") {
tokenizer._currentToken = URLToken.scheme(schemeOrHost);
tokenizer._currentState = "HOST";
} else {
tokenizer._currentToken = URLToken.host(schemeOrHost);
tokenizer._currentState = "PORT";
}
} else {
tokenizer._currentToken = URLToken.host(schemeOrHost);
if (getCurrentCharacter(tokenizer) === "/") {
tokenizer._currentState = "PATH";
} else {
tokenizer._currentState = "QUERY";
}
}
}
function nextHost(tokenizer: URLTokenizer): void {
if (peekCharacters(tokenizer, 3) === "://") {
nextCharacter(tokenizer, 3);
}
const host: string = readUntilCharacter(tokenizer, ":", "/", "?");
tokenizer._currentToken = URLToken.host(host);
if (!hasCurrentCharacter(tokenizer)) {
tokenizer._currentState = "DONE";
} else if (getCurrentCharacter(tokenizer) === ":") {
tokenizer._currentState = "PORT";
} else if (getCurrentCharacter(tokenizer) === "/") {
tokenizer._currentState = "PATH";
} else {
tokenizer._currentState = "QUERY";
}
}
function nextPort(tokenizer: URLTokenizer): void {
if (getCurrentCharacter(tokenizer) === ":") {
nextCharacter(tokenizer);
}
const port: string = readUntilCharacter(tokenizer, "/", "?");
tokenizer._currentToken = URLToken.port(port);
if (!hasCurrentCharacter(tokenizer)) {
tokenizer._currentState = "DONE";
} else if (getCurrentCharacter(tokenizer) === "/") {
tokenizer._currentState = "PATH";
} else {
tokenizer._currentState = "QUERY";
}
}
function nextPath(tokenizer: URLTokenizer): void {
const path: string = readUntilCharacter(tokenizer, "?");
tokenizer._currentToken = URLToken.path(path);
if (!hasCurrentCharacter(tokenizer)) {
tokenizer._currentState = "DONE";
} else {
tokenizer._currentState = "QUERY";
}
}
function nextQuery(tokenizer: URLTokenizer): void {
if (getCurrentCharacter(tokenizer) === "?") {
nextCharacter(tokenizer);
}
const query: string = readRemaining(tokenizer);
tokenizer._currentToken = URLToken.query(query);
tokenizer._currentState = "DONE";
}

View file

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* Encodes a string in base64 format.
* @param value the string to encode
*/
export function encodeString(value: string): string {
return btoa(value);
}
/**
* Encodes a byte array in base64 format.
* @param value the Uint8Aray to encode
*/
export function encodeByteArray(value: Uint8Array): string {
let str = "";
for (let i = 0; i < value.length; i++) {
str += String.fromCharCode(value[i]);
}
return btoa(str);
}
/**
* Decodes a base64 string into a byte array.
* @param value the base64 string to decode
*/
export function decodeString(value: string): Uint8Array {
const byteString = atob(value);
const arr = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
arr[i] = byteString.charCodeAt(i);
}
return arr;
}

29
node_modules/@azure/ms-rest-js/lib/util/base64.ts generated vendored Normal file
View file

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
/**
* Encodes a string in base64 format.
* @param value the string to encode
*/
export function encodeString(value: string): string {
return Buffer.from(value).toString("base64");
}
/**
* Encodes a byte array in base64 format.
* @param value the Uint8Aray to encode
*/
export function encodeByteArray(value: Uint8Array): string {
// Buffer.from accepts <ArrayBuffer> | <SharedArrayBuffer>-- the TypeScript definition is off here
// https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
const bufferValue = value instanceof Buffer ? value : Buffer.from(value.buffer as ArrayBuffer);
return bufferValue.toString("base64");
}
/**
* Decodes a base64 string into a byte array.
* @param value the base64 string to decode
*/
export function decodeString(value: string): Uint8Array {
return Buffer.from(value, "base64");
}

108
node_modules/@azure/ms-rest-js/lib/util/constants.ts generated vendored Normal file
View file

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
export const Constants = {
/**
* The ms-rest version
* @const
* @type {string}
*/
msRestVersion: "2.6.1",
/**
* Specifies HTTP.
*
* @const
* @type {string}
*/
HTTP: "http:",
/**
* Specifies HTTPS.
*
* @const
* @type {string}
*/
HTTPS: "https:",
/**
* Specifies HTTP Proxy.
*
* @const
* @type {string}
*/
HTTP_PROXY: "HTTP_PROXY",
/**
* Specifies HTTPS Proxy.
*
* @const
* @type {string}
*/
HTTPS_PROXY: "HTTPS_PROXY",
/**
* Specifies NO Proxy.
*/
NO_PROXY: "NO_PROXY",
/**
* Specifies ALL Proxy.
*/
ALL_PROXY: "ALL_PROXY",
HttpConstants: {
/**
* Http Verbs
*
* @const
* @enum {string}
*/
HttpVerbs: {
PUT: "PUT",
GET: "GET",
DELETE: "DELETE",
POST: "POST",
MERGE: "MERGE",
HEAD: "HEAD",
PATCH: "PATCH",
},
StatusCodes: {
TooManyRequests: 429,
},
},
/**
* Defines constants for use with HTTP headers.
*/
HeaderConstants: {
/**
* The Authorization header.
*
* @const
* @type {string}
*/
AUTHORIZATION: "authorization",
AUTHORIZATION_SCHEME: "Bearer",
/**
* The Retry-After response-header field can be used with a 503 (Service
* Unavailable) or 349 (Too Many Requests) responses to indicate how long
* the service is expected to be unavailable to the requesting client.
*
* @const
* @type {string}
*/
RETRY_AFTER: "Retry-After",
/**
* The UserAgent header.
*
* @const
* @type {string}
*/
USER_AGENT: "User-Agent",
},
};

288
node_modules/@azure/ms-rest-js/lib/util/utils.ts generated vendored Normal file
View file

@ -0,0 +1,288 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { v4 as uuidv4 } from "uuid";
import { HttpOperationResponse } from "../httpOperationResponse";
import { RestError } from "../restError";
import { WebResourceLike } from "../webResource";
import { Constants } from "./constants";
/**
* A constant that indicates whether the environment is node.js or browser based.
*/
export const isNode =
typeof process !== "undefined" &&
!!process.version &&
!!process.versions &&
!!process.versions.node;
/**
* Checks if a parsed URL is HTTPS
*
* @param {object} urlToCheck The url to check
* @return {boolean} True if the URL is HTTPS; false otherwise.
*/
export function urlIsHTTPS(urlToCheck: { protocol: string }): boolean {
return urlToCheck.protocol.toLowerCase() === Constants.HTTPS;
}
/**
* Encodes an URI.
*
* @param {string} uri The URI to be encoded.
* @return {string} The encoded URI.
*/
export function encodeUri(uri: string): string {
return encodeURIComponent(uri)
.replace(/!/g, "%21")
.replace(/"/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29")
.replace(/\*/g, "%2A");
}
/**
* Returns a stripped version of the Http Response which only contains body,
* headers and the status.
*
* @param {HttpOperationResponse} response The Http Response
*
* @return {object} The stripped version of Http Response.
*/
export function stripResponse(response: HttpOperationResponse): any {
const strippedResponse: any = {};
strippedResponse.body = response.bodyAsText;
strippedResponse.headers = response.headers;
strippedResponse.status = response.status;
return strippedResponse;
}
/**
* Returns a stripped version of the Http Request that does not contain the
* Authorization header.
*
* @param {WebResource} request The Http Request object
*
* @return {WebResource} The stripped version of Http Request.
*/
export function stripRequest(request: WebResourceLike): WebResourceLike {
const strippedRequest = request.clone();
if (strippedRequest.headers) {
strippedRequest.headers.remove("authorization");
}
return strippedRequest;
}
/**
* Validates the given uuid as a string
*
* @param {string} uuid The uuid as a string that needs to be validated
*
* @return {boolean} True if the uuid is valid; false otherwise.
*/
export function isValidUuid(uuid: string): boolean {
const validUuidRegex = new RegExp(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
"ig"
);
return validUuidRegex.test(uuid);
}
/**
* Provides an array of values of an object. For example
* for a given object { "a": "foo", "b": "bar" }, the method returns ["foo", "bar"].
*
* @param {object} obj An object whose properties need to be enumerated so that it"s values can be provided as an array
*
* @return {any[]} An array of values of the given object.
*/
export function objectValues(obj: { [key: string]: any }): any[] {
const result: any[] = [];
if (obj && obj instanceof Object) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result.push((<any>obj)[key]);
}
}
} else {
throw new Error(
`The provided object ${JSON.stringify(
obj,
undefined,
2
)} is not a valid object that can be ` + `enumerated to provide its values as an array.`
);
}
return result;
}
/**
* Generated UUID
*
* @return {string} RFC4122 v4 UUID.
*/
export function generateUuid(): string {
return uuidv4();
}
/**
* Executes an array of promises sequentially. Inspiration of this method is here:
* https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html. An awesome blog on promises!
*
* @param {Array} promiseFactories An array of promise factories(A function that return a promise)
*
* @param {any} [kickstart] Input to the first promise that is used to kickstart the promise chain.
* If not provided then the promise chain starts with undefined.
*
* @return A chain of resolved or rejected promises
*/
export function executePromisesSequentially(promiseFactories: Array<any>, kickstart: any) {
let result = Promise.resolve(kickstart);
promiseFactories.forEach((promiseFactory) => {
result = result.then(promiseFactory);
});
return result;
}
/**
* Merges source object into the target object
* @param {object} source The object that needs to be merged
*
* @param {object} target The object to be merged into
*
* @returns {object} Returns the merged target object.
*/
export function mergeObjects(source: { [key: string]: any }, target: { [key: string]: any }) {
Object.keys(source).forEach((key) => {
target[key] = source[key];
});
return target;
}
/**
* A wrapper for setTimeout that resolves a promise after t milliseconds.
* @param {number} t The number of milliseconds to be delayed.
* @param {T} value The value to be resolved with after a timeout of t milliseconds.
* @returns {Promise<T>} Resolved promise
*/
export function delay<T>(t: number, value?: T): Promise<T> {
return new Promise((resolve) => setTimeout(() => resolve(value), t));
}
/**
* Service callback that is returned for REST requests initiated by the service client.
*/
export interface ServiceCallback<TResult> {
/**
* A method that will be invoked as a callback to a service function.
* @param {Error | RestError | null} err The error occurred if any, while executing the request; otherwise null.
* @param {TResult} [result] The deserialized response body if an error did not occur.
* @param {WebResourceLike} [request] The raw/actual request sent to the server if an error did not occur.
* @param {HttpOperationResponse} [response] The raw/actual response from the server if an error did not occur.
*/
(
err: Error | RestError | null,
result?: TResult,
request?: WebResourceLike,
response?: HttpOperationResponse
): void;
}
/**
* Converts a Promise to a callback.
* @param {Promise<any>} promise The Promise to be converted to a callback
* @returns {Function} A function that takes the callback (cb: Function): void
* @deprecated generated code should instead depend on responseToBody
*/
export function promiseToCallback(promise: Promise<any>): Function {
if (typeof promise.then !== "function") {
throw new Error("The provided input is not a Promise.");
}
return (cb: Function): void => {
promise.then(
(data: any) => {
cb(undefined, data);
},
(err: Error) => {
cb(err);
}
);
};
}
/**
* Converts a Promise to a service callback.
* @param {Promise<HttpOperationResponse>} promise - The Promise of HttpOperationResponse to be converted to a service callback
* @returns {Function} A function that takes the service callback (cb: ServiceCallback<T>): void
*/
export function promiseToServiceCallback<T>(promise: Promise<HttpOperationResponse>): Function {
if (typeof promise.then !== "function") {
throw new Error("The provided input is not a Promise.");
}
return (cb: ServiceCallback<T>): void => {
promise.then(
(data: HttpOperationResponse) => {
process.nextTick(cb, undefined, data.parsedBody as T, data.request, data);
},
(err: Error) => {
process.nextTick(cb, err);
}
);
};
}
export function prepareXMLRootList(obj: any, elementName: string) {
if (!Array.isArray(obj)) {
obj = [obj];
}
return { [elementName]: obj };
}
/**
* Applies the properties on the prototype of sourceCtors to the prototype of targetCtor
* @param {object} targetCtor The target object on which the properties need to be applied.
* @param {Array<object>} sourceCtors An array of source objects from which the properties need to be taken.
*/
export function applyMixins(targetCtor: any, sourceCtors: any[]): void {
sourceCtors.forEach((sourceCtors) => {
Object.getOwnPropertyNames(sourceCtors.prototype).forEach((name) => {
targetCtor.prototype[name] = sourceCtors.prototype[name];
});
});
}
const validateISODuration = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
/**
* Indicates whether the given string is in ISO 8601 format.
* @param {string} value The value to be validated for ISO 8601 duration format.
* @return {boolean} `true` if valid, `false` otherwise.
*/
export function isDuration(value: string): boolean {
return validateISODuration.test(value);
}
/**
* Replace all of the instances of searchValue in value with the provided replaceValue.
* @param {string | undefined} value The value to search and replace in.
* @param {string} searchValue The value to search for in the value argument.
* @param {string} replaceValue The value to replace searchValue with in the value argument.
* @returns {string | undefined} The value where each instance of searchValue was replaced with replacedValue.
*/
export function replaceAll(
value: string | undefined,
searchValue: string,
replaceValue: string
): string | undefined {
return !value || !searchValue ? value : value.split(searchValue).join(replaceValue || "");
}
/**
* Determines whether the given enity is a basic/primitive type
* (string, number, boolean, null, undefined).
* @param value Any entity
* @return boolean - true is it is primitive type, false otherwise.
*/
export function isPrimitiveType(value: any): boolean {
return (typeof value !== "object" && typeof value !== "function") || value === null;
}

149
node_modules/@azure/ms-rest-js/lib/util/xml.browser.ts generated vendored Normal file
View file

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
const parser = new DOMParser();
export function parseXML(str: string): Promise<any> {
try {
const dom = parser.parseFromString(str, "application/xml");
throwIfError(dom);
const obj = domToObject(dom.childNodes[0]);
return Promise.resolve(obj);
} catch (err) {
return Promise.reject(err);
}
}
let errorNS = "";
try {
errorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0]
.namespaceURI!;
} catch (ignored) {
// Most browsers will return a document containing <parsererror>, but IE will throw.
}
function throwIfError(dom: Document) {
if (errorNS) {
const parserErrors = dom.getElementsByTagNameNS(errorNS, "parsererror");
if (parserErrors.length) {
throw new Error(parserErrors.item(0)!.innerHTML);
}
}
}
function isElement(node: Node): node is Element {
return !!(node as Element).attributes;
}
/**
* Get the Element-typed version of the provided Node if the provided node is an element with
* attributes. If it isn't, then undefined is returned.
*/
function asElementWithAttributes(node: Node): Element | undefined {
return isElement(node) && node.hasAttributes() ? node : undefined;
}
function domToObject(node: Node): any {
let result: any = {};
const childNodeCount: number = node.childNodes.length;
const firstChildNode: Node = node.childNodes[0];
const onlyChildTextValue: string | undefined =
(firstChildNode &&
childNodeCount === 1 &&
firstChildNode.nodeType === Node.TEXT_NODE &&
firstChildNode.nodeValue) ||
undefined;
const elementWithAttributes: Element | undefined = asElementWithAttributes(node);
if (elementWithAttributes) {
result["$"] = {};
for (let i = 0; i < elementWithAttributes.attributes.length; i++) {
const attr = elementWithAttributes.attributes[i];
result["$"][attr.nodeName] = attr.nodeValue;
}
if (onlyChildTextValue) {
result["_"] = onlyChildTextValue;
}
} else if (childNodeCount === 0) {
result = "";
} else if (onlyChildTextValue) {
result = onlyChildTextValue;
}
if (!onlyChildTextValue) {
for (let i = 0; i < childNodeCount; i++) {
const child = node.childNodes[i];
// Ignore leading/trailing whitespace nodes
if (child.nodeType !== Node.TEXT_NODE) {
const childObject: any = domToObject(child);
if (!result[child.nodeName]) {
result[child.nodeName] = childObject;
} else if (Array.isArray(result[child.nodeName])) {
result[child.nodeName].push(childObject);
} else {
result[child.nodeName] = [result[child.nodeName], childObject];
}
}
}
}
return result;
}
// tslint:disable-next-line:no-null-keyword
const doc = document.implementation.createDocument(null, null, null);
const serializer = new XMLSerializer();
export function stringifyXML(obj: any, opts?: { rootName?: string }) {
const rootName = (opts && opts.rootName) || "root";
const dom = buildNode(obj, rootName)[0];
return (
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + serializer.serializeToString(dom)
);
}
function buildAttributes(attrs: { [key: string]: { toString(): string } }): Attr[] {
const result = [];
for (const key of Object.keys(attrs)) {
const attr = doc.createAttribute(key);
attr.value = attrs[key].toString();
result.push(attr);
}
return result;
}
function buildNode(obj: any, elementName: string): Node[] {
if (typeof obj === "string" || typeof obj === "number" || typeof obj === "boolean") {
const elem = doc.createElement(elementName);
elem.textContent = obj.toString();
return [elem];
} else if (Array.isArray(obj)) {
const result = [];
for (const arrayElem of obj) {
for (const child of buildNode(arrayElem, elementName)) {
result.push(child);
}
}
return result;
} else if (typeof obj === "object") {
const elem = doc.createElement(elementName);
for (const key of Object.keys(obj)) {
if (key === "$") {
for (const attr of buildAttributes(obj[key])) {
elem.attributes.setNamedItem(attr);
}
} else {
for (const child of buildNode(obj[key], key)) {
elem.appendChild(child);
}
}
}
return [elem];
} else {
throw new Error(`Illegal value passed to buildObject: ${obj}`);
}
}

35
node_modules/@azure/ms-rest-js/lib/util/xml.ts generated vendored Normal file
View file

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as xml2js from "xml2js";
export function stringifyXML(obj: any, opts?: { rootName?: string }) {
const builder = new xml2js.Builder({
rootName: (opts || {}).rootName,
renderOpts: {
pretty: false,
},
});
return builder.buildObject(obj);
}
export function parseXML(str: string): Promise<any> {
const xmlParser = new xml2js.Parser({
explicitArray: false,
explicitCharkey: false,
explicitRoot: false,
});
return new Promise((resolve, reject) => {
if (!str) {
reject(new Error("Document is empty"));
} else {
xmlParser.parseString(str, (err?: Error, res?: any) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
}
});
}

668
node_modules/@azure/ms-rest-js/lib/webResource.ts generated vendored Normal file
View file

@ -0,0 +1,668 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpHeaders, HttpHeadersLike, isHttpHeadersLike } from "./httpHeaders";
import { OperationSpec } from "./operationSpec";
import { Mapper, Serializer } from "./serializer";
import { generateUuid } from "./util/utils";
import { HttpOperationResponse } from "./httpOperationResponse";
import { OperationResponse } from "./operationResponse";
import { AgentSettings, ProxySettings } from "./serviceClient";
export type HttpMethods =
| "GET"
| "PUT"
| "POST"
| "DELETE"
| "PATCH"
| "HEAD"
| "OPTIONS"
| "TRACE";
export type HttpRequestBody =
| Blob
| string
| ArrayBuffer
| ArrayBufferView
| (() => NodeJS.ReadableStream);
/**
* Fired in response to upload or download progress.
*/
export type TransferProgressEvent = {
/**
* The number of bytes loaded so far.
*/
loadedBytes: number;
};
/**
* Allows the request to be aborted upon firing of the "abort" event.
* Compatible with the browser built-in AbortSignal and common polyfills.
*/
export interface AbortSignalLike {
readonly aborted: boolean;
dispatchEvent: (event: Event) => boolean;
onabort: ((this: AbortSignalLike, ev: Event) => any) | null;
addEventListener: (
type: "abort",
listener: (this: AbortSignalLike, ev: Event) => any,
options?: any
) => void;
removeEventListener: (
type: "abort",
listener: (this: AbortSignalLike, ev: Event) => any,
options?: any
) => void;
}
/**
* An abstraction over a REST call.
*/
export interface WebResourceLike {
/**
* The URL being accessed by the request.
*/
url: string;
/**
* The HTTP method to use when making the request.
*/
method: HttpMethods;
/**
* The HTTP body contents of the request.
*/
body?: any;
/**
* The HTTP headers to use when making the request.
*/
headers: HttpHeadersLike;
/**
* Whether or not the body of the HttpOperationResponse should be treated as a stream.
*/
streamResponseBody?: boolean;
/**
* Whether or not the HttpOperationResponse should be deserialized. If this is undefined, then the
* HttpOperationResponse should be deserialized.
*/
shouldDeserialize?: boolean | ((response: HttpOperationResponse) => boolean);
/**
* A function that returns the proper OperationResponse for the given OperationSpec and
* HttpOperationResponse combination. If this is undefined, then a simple status code lookup will
* be used.
*/
operationResponseGetter?: (
operationSpec: OperationSpec,
response: HttpOperationResponse
) => undefined | OperationResponse;
formData?: any;
/**
* A query string represented as an object.
*/
query?: { [key: string]: any };
/**
* Used to parse the response.
*/
operationSpec?: OperationSpec;
/**
* If credentials (cookies) should be sent along during an XHR.
*/
withCredentials: boolean;
/**
* The number of milliseconds a request can take before automatically being terminated.
* If the request is terminated, an `AbortError` is thrown.
*/
timeout: number;
/**
* Proxy configuration.
*/
proxySettings?: ProxySettings;
/**
* HTTP(S) agent configuration.
*/
agentSettings?: AgentSettings;
/**
* If the connection should be reused.
*/
keepAlive?: boolean;
/**
* Limit the number of redirects followed for this request. If set to 0, redirects will not be followed.
* If left undefined the default redirect behaviour of the underlying node_fetch will apply.
*/
redirectLimit?: number;
/**
* Used to abort the request later.
*/
abortSignal?: AbortSignalLike;
/**
* Callback which fires upon upload progress.
*/
onUploadProgress?: (progress: TransferProgressEvent) => void;
/** Callback which fires upon download progress. */
onDownloadProgress?: (progress: TransferProgressEvent) => void;
/**
* Validates that the required properties such as method, url, headers["Content-Type"],
* headers["accept-language"] are defined. It will throw an error if one of the above
* mentioned properties are not defined.
*/
validateRequestProperties(): void;
/**
* Sets options on the request.
*/
prepare(options: RequestPrepareOptions): WebResourceLike;
/**
* Clone this request object.
*/
clone(): WebResourceLike;
}
export function isWebResourceLike(object: any): object is WebResourceLike {
if (typeof object !== "object") {
return false;
}
if (
typeof object.url === "string" &&
typeof object.method === "string" &&
typeof object.headers === "object" &&
isHttpHeadersLike(object.headers) &&
typeof object.validateRequestProperties === "function" &&
typeof object.prepare === "function" &&
typeof object.clone === "function"
) {
return true;
}
return false;
}
/**
* Creates a new WebResource object.
*
* This class provides an abstraction over a REST call by being library / implementation agnostic and wrapping the necessary
* properties to initiate a request.
*
* @constructor
*/
export class WebResource {
url: string;
method: HttpMethods;
body?: any;
headers: HttpHeadersLike;
/**
* Whether or not the body of the HttpOperationResponse should be treated as a stream.
*/
streamResponseBody?: boolean;
/**
* Whether or not the HttpOperationResponse should be deserialized. If this is undefined, then the
* HttpOperationResponse should be deserialized.
*/
shouldDeserialize?: boolean | ((response: HttpOperationResponse) => boolean);
/**
* A function that returns the proper OperationResponse for the given OperationSpec and
* HttpOperationResponse combination. If this is undefined, then a simple status code lookup will
* be used.
*/
operationResponseGetter?: (
operationSpec: OperationSpec,
response: HttpOperationResponse
) => undefined | OperationResponse;
formData?: any;
query?: { [key: string]: any };
operationSpec?: OperationSpec;
withCredentials: boolean;
timeout: number;
proxySettings?: ProxySettings;
keepAlive?: boolean;
agentSettings?: AgentSettings;
redirectLimit?: number;
abortSignal?: AbortSignalLike;
/** Callback which fires upon upload progress. */
onUploadProgress?: (progress: TransferProgressEvent) => void;
/** Callback which fires upon download progress. */
onDownloadProgress?: (progress: TransferProgressEvent) => void;
constructor(
url?: string,
method?: HttpMethods,
body?: any,
query?: { [key: string]: any },
headers?: { [key: string]: any } | HttpHeadersLike,
streamResponseBody?: boolean,
withCredentials?: boolean,
abortSignal?: AbortSignalLike,
timeout?: number,
onUploadProgress?: (progress: TransferProgressEvent) => void,
onDownloadProgress?: (progress: TransferProgressEvent) => void,
proxySettings?: ProxySettings,
keepAlive?: boolean,
agentSettings?: AgentSettings,
redirectLimit?: number
) {
this.streamResponseBody = streamResponseBody;
this.url = url || "";
this.method = method || "GET";
this.headers = isHttpHeadersLike(headers) ? headers : new HttpHeaders(headers);
this.body = body;
this.query = query;
this.formData = undefined;
this.withCredentials = withCredentials || false;
this.abortSignal = abortSignal;
this.timeout = timeout || 0;
this.onUploadProgress = onUploadProgress;
this.onDownloadProgress = onDownloadProgress;
this.proxySettings = proxySettings;
this.keepAlive = keepAlive;
this.agentSettings = agentSettings;
this.redirectLimit = redirectLimit;
}
/**
* Validates that the required properties such as method, url, headers["Content-Type"],
* headers["accept-language"] are defined. It will throw an error if one of the above
* mentioned properties are not defined.
*/
validateRequestProperties(): void {
if (!this.method) {
throw new Error("WebResource.method is required.");
}
if (!this.url) {
throw new Error("WebResource.url is required.");
}
}
/**
* Prepares the request.
* @param {RequestPrepareOptions} options Options to provide for preparing the request.
* @returns {WebResource} Returns the prepared WebResource (HTTP Request) object that needs to be given to the request pipeline.
*/
prepare(options: RequestPrepareOptions): WebResource {
if (!options) {
throw new Error("options object is required");
}
if (options.method == undefined || typeof options.method.valueOf() !== "string") {
throw new Error("options.method must be a string.");
}
if (options.url && options.pathTemplate) {
throw new Error(
"options.url and options.pathTemplate are mutually exclusive. Please provide exactly one of them."
);
}
if (
(options.pathTemplate == undefined || typeof options.pathTemplate.valueOf() !== "string") &&
(options.url == undefined || typeof options.url.valueOf() !== "string")
) {
throw new Error("Please provide exactly one of options.pathTemplate or options.url.");
}
// set the url if it is provided.
if (options.url) {
if (typeof options.url !== "string") {
throw new Error('options.url must be of type "string".');
}
this.url = options.url;
}
// set the method
if (options.method) {
const validMethods = ["GET", "PUT", "HEAD", "DELETE", "OPTIONS", "POST", "PATCH", "TRACE"];
if (validMethods.indexOf(options.method.toUpperCase()) === -1) {
throw new Error(
'The provided method "' +
options.method +
'" is invalid. Supported HTTP methods are: ' +
JSON.stringify(validMethods)
);
}
}
this.method = options.method.toUpperCase() as HttpMethods;
// construct the url if path template is provided
if (options.pathTemplate) {
const { pathTemplate, pathParameters } = options;
if (typeof pathTemplate !== "string") {
throw new Error('options.pathTemplate must be of type "string".');
}
if (!options.baseUrl) {
options.baseUrl = "https://management.azure.com";
}
const baseUrl = options.baseUrl;
let url =
baseUrl +
(baseUrl.endsWith("/") ? "" : "/") +
(pathTemplate.startsWith("/") ? pathTemplate.slice(1) : pathTemplate);
const segments = url.match(/({\w*\s*\w*})/gi);
if (segments && segments.length) {
if (!pathParameters) {
throw new Error(
`pathTemplate: ${pathTemplate} has been provided. Hence, options.pathParameters must also be provided.`
);
}
segments.forEach(function (item) {
const pathParamName = item.slice(1, -1);
const pathParam = (pathParameters as { [key: string]: any })[pathParamName];
if (
pathParam === null ||
pathParam === undefined ||
!(typeof pathParam === "string" || typeof pathParam === "object")
) {
throw new Error(
`pathTemplate: ${pathTemplate} contains the path parameter ${pathParamName}` +
` however, it is not present in ${pathParameters} - ${JSON.stringify(
pathParameters,
undefined,
2
)}.` +
`The value of the path parameter can either be a "string" of the form { ${pathParamName}: "some sample value" } or ` +
`it can be an "object" of the form { "${pathParamName}": { value: "some sample value", skipUrlEncoding: true } }.`
);
}
if (typeof pathParam.valueOf() === "string") {
url = url.replace(item, encodeURIComponent(pathParam));
}
if (typeof pathParam.valueOf() === "object") {
if (!pathParam.value) {
throw new Error(
`options.pathParameters[${pathParamName}] is of type "object" but it does not contain a "value" property.`
);
}
if (pathParam.skipUrlEncoding) {
url = url.replace(item, pathParam.value);
} else {
url = url.replace(item, encodeURIComponent(pathParam.value));
}
}
});
}
this.url = url;
}
// append query parameters to the url if they are provided. They can be provided with pathTemplate or url option.
if (options.queryParameters) {
const queryParameters = options.queryParameters;
if (typeof queryParameters !== "object") {
throw new Error(
`options.queryParameters must be of type object. It should be a JSON object ` +
`of "query-parameter-name" as the key and the "query-parameter-value" as the value. ` +
`The "query-parameter-value" may be fo type "string" or an "object" of the form { value: "query-parameter-value", skipUrlEncoding: true }.`
);
}
// append question mark if it is not present in the url
if (this.url && this.url.indexOf("?") === -1) {
this.url += "?";
}
// construct queryString
const queryParams = [];
// We need to populate this.query as a dictionary if the request is being used for Sway's validateRequest().
this.query = {};
for (const queryParamName in queryParameters) {
const queryParam: any = queryParameters[queryParamName];
if (queryParam) {
if (typeof queryParam === "string") {
queryParams.push(queryParamName + "=" + encodeURIComponent(queryParam));
this.query[queryParamName] = encodeURIComponent(queryParam);
} else if (typeof queryParam === "object") {
if (!queryParam.value) {
throw new Error(
`options.queryParameters[${queryParamName}] is of type "object" but it does not contain a "value" property.`
);
}
if (queryParam.skipUrlEncoding) {
queryParams.push(queryParamName + "=" + queryParam.value);
this.query[queryParamName] = queryParam.value;
} else {
queryParams.push(queryParamName + "=" + encodeURIComponent(queryParam.value));
this.query[queryParamName] = encodeURIComponent(queryParam.value);
}
}
}
} // end-of-for
// append the queryString
this.url += queryParams.join("&");
}
// add headers to the request if they are provided
if (options.headers) {
const headers = options.headers;
for (const headerName of Object.keys(options.headers)) {
this.headers.set(headerName, headers[headerName]);
}
}
// ensure accept-language is set correctly
if (!this.headers.get("accept-language")) {
this.headers.set("accept-language", "en-US");
}
// ensure the request-id is set correctly
if (!this.headers.get("x-ms-client-request-id") && !options.disableClientRequestId) {
this.headers.set("x-ms-client-request-id", generateUuid());
}
// default
if (!this.headers.get("Content-Type")) {
this.headers.set("Content-Type", "application/json; charset=utf-8");
}
// set the request body. request.js automatically sets the Content-Length request header, so we need not set it explicilty
this.body = options.body;
if (options.body != undefined) {
// body as a stream special case. set the body as-is and check for some special request headers specific to sending a stream.
if (options.bodyIsStream) {
if (!this.headers.get("Transfer-Encoding")) {
this.headers.set("Transfer-Encoding", "chunked");
}
if (this.headers.get("Content-Type") !== "application/octet-stream") {
this.headers.set("Content-Type", "application/octet-stream");
}
} else {
if (options.serializationMapper) {
this.body = new Serializer(options.mappers).serialize(
options.serializationMapper,
options.body,
"requestBody"
);
}
if (!options.disableJsonStringifyOnBody) {
this.body = JSON.stringify(options.body);
}
}
}
this.abortSignal = options.abortSignal;
this.onDownloadProgress = options.onDownloadProgress;
this.onUploadProgress = options.onUploadProgress;
this.redirectLimit = options.redirectLimit;
this.streamResponseBody = options.streamResponseBody;
return this;
}
/**
* Clone this WebResource HTTP request object.
* @returns {WebResource} The clone of this WebResource HTTP request object.
*/
clone(): WebResource {
const result = new WebResource(
this.url,
this.method,
this.body,
this.query,
this.headers && this.headers.clone(),
this.streamResponseBody,
this.withCredentials,
this.abortSignal,
this.timeout,
this.onUploadProgress,
this.onDownloadProgress,
this.proxySettings,
this.keepAlive,
this.agentSettings,
this.redirectLimit
);
if (this.formData) {
result.formData = this.formData;
}
if (this.operationSpec) {
result.operationSpec = this.operationSpec;
}
if (this.shouldDeserialize) {
result.shouldDeserialize = this.shouldDeserialize;
}
if (this.operationResponseGetter) {
result.operationResponseGetter = this.operationResponseGetter;
}
return result;
}
}
export interface RequestPrepareOptions {
/**
* The HTTP request method. Valid values are "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "POST",
* or "PATCH".
*/
method: HttpMethods;
/**
* The request url. It may or may not have query parameters in it. Either provide the "url" or
* provide the "pathTemplate" in the options object. Both the options are mutually exclusive.
*/
url?: string;
/**
* A dictionary of query parameters to be appended to the url, where
* the "key" is the "query-parameter-name" and the "value" is the "query-parameter-value".
* The "query-parameter-value" can be of type "string" or it can be of type "object".
* The "object" format should be used when you want to skip url encoding. While using the object format,
* the object must have a property named value which provides the "query-parameter-value".
* Example:
* - query-parameter-value in "object" format: { "query-parameter-name": { value: "query-parameter-value", skipUrlEncoding: true } }
* - query-parameter-value in "string" format: { "query-parameter-name": "query-parameter-value"}.
* Note: "If options.url already has some query parameters, then the value provided in options.queryParameters will be appended to the url.
*/
queryParameters?: { [key: string]: any | ParameterValue };
/**
* The path template of the request url. Either provide the "url" or provide the "pathTemplate" in
* the options object. Both the options are mutually exclusive.
* Example: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}"
*/
pathTemplate?: string;
/**
* The base url of the request. Default value is: "https://management.azure.com". This is
* applicable only with pathTemplate. If you are providing options.url then it is expected that
* you provide the complete url.
*/
baseUrl?: string;
/**
* A dictionary of path parameters that need to be replaced with actual values in the pathTemplate.
* Here the key is the "path-parameter-name" and the value is the "path-parameter-value".
* The "path-parameter-value" can be of type "string" or it can be of type "object".
* The "object" format should be used when you want to skip url encoding. While using the object format,
* the object must have a property named value which provides the "path-parameter-value".
* Example:
* - path-parameter-value in "object" format: { "path-parameter-name": { value: "path-parameter-value", skipUrlEncoding: true } }
* - path-parameter-value in "string" format: { "path-parameter-name": "path-parameter-value" }.
*/
pathParameters?: { [key: string]: any | ParameterValue };
formData?: { [key: string]: any };
/**
* A dictionary of request headers that need to be applied to the request.
* Here the key is the "header-name" and the value is the "header-value". The header-value MUST be of type string.
* - ContentType must be provided with the key name as "Content-Type". Default value "application/json; charset=utf-8".
* - "Transfer-Encoding" is set to "chunked" by default if "options.bodyIsStream" is set to true.
* - "Content-Type" is set to "application/octet-stream" by default if "options.bodyIsStream" is set to true.
* - "accept-language" by default is set to "en-US"
* - "x-ms-client-request-id" by default is set to a new Guid. To not generate a guid for the request, please set options.disableClientRequestId to true
*/
headers?: { [key: string]: any };
/**
* When set to true, instructs the client to not set "x-ms-client-request-id" header to a new Guid().
*/
disableClientRequestId?: boolean;
/**
* The request body. It can be of any type. This value will be serialized if it is not a stream.
*/
body?: any;
/**
* Provides information on how to serialize the request body.
*/
serializationMapper?: Mapper;
/**
* A dictionary of mappers that may be used while [de]serialization.
*/
mappers?: { [x: string]: any };
/**
* Provides information on how to deserialize the response body.
*/
deserializationMapper?: object;
/**
* Indicates whether this method should JSON.stringify() the request body. Default value: false.
*/
disableJsonStringifyOnBody?: boolean;
/**
* Indicates whether the request body is a stream (useful for file upload scenarios).
*/
bodyIsStream?: boolean;
abortSignal?: AbortSignalLike;
/**
* Limit the number of redirects followed for this request. If set to 0, redirects will not be followed.
* If left undefined the default redirect behaviour of the underlying node_fetch will apply.
*/
redirectLimit?: number;
onUploadProgress?: (progress: TransferProgressEvent) => void;
onDownloadProgress?: (progress: TransferProgressEvent) => void;
streamResponseBody?: boolean;
}
/**
* The Parameter value provided for path or query parameters in RequestPrepareOptions
*/
export interface ParameterValue {
value: any;
skipUrlEncoding: boolean;
[key: string]: any;
}
/**
* Describes the base structure of the options object that will be used in every operation.
*/
export interface RequestOptionsBase {
/**
* @property {object} [customHeaders] User defined custom request headers that
* will be applied before the request is sent.
*/
customHeaders?: { [key: string]: string };
/**
* The signal which can be used to abort requests.
*/
abortSignal?: AbortSignalLike;
/**
* The number of milliseconds a request can take before automatically being terminated.
*/
timeout?: number;
/**
* Callback which fires upon upload progress.
*/
onUploadProgress?: (progress: TransferProgressEvent) => void;
/**
* Callback which fires upon download progress.
*/
onDownloadProgress?: (progress: TransferProgressEvent) => void;
[key: string]: any;
}

178
node_modules/@azure/ms-rest-js/lib/xhrHttpClient.ts generated vendored Normal file
View file

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { HttpClient } from "./httpClient";
import { HttpHeaders } from "./httpHeaders";
import { WebResourceLike, TransferProgressEvent } from "./webResource";
import { HttpOperationResponse } from "./httpOperationResponse";
import { RestError } from "./restError";
/**
* A HttpClient implementation that uses XMLHttpRequest to send HTTP requests.
*/
export class XhrHttpClient implements HttpClient {
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
const xhr = new XMLHttpRequest();
if (request.agentSettings) {
throw new Error("HTTP agent settings not supported in browser environment");
}
if (request.proxySettings) {
throw new Error("HTTP proxy is not supported in browser environment");
}
const abortSignal = request.abortSignal;
if (abortSignal) {
const listener = () => {
xhr.abort();
};
abortSignal.addEventListener("abort", listener);
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
abortSignal.removeEventListener("abort", listener);
}
});
}
addProgressListener(xhr.upload, request.onUploadProgress);
addProgressListener(xhr, request.onDownloadProgress);
if (request.formData) {
const formData = request.formData;
const requestForm = new FormData();
const appendFormValue = (key: string, value: any) => {
if (value && value.hasOwnProperty("value") && value.hasOwnProperty("options")) {
requestForm.append(key, value.value, value.options);
} else {
requestForm.append(key, value);
}
};
for (const formKey of Object.keys(formData)) {
const formValue = formData[formKey];
if (Array.isArray(formValue)) {
for (let j = 0; j < formValue.length; j++) {
appendFormValue(formKey, formValue[j]);
}
} else {
appendFormValue(formKey, formValue);
}
}
request.body = requestForm;
request.formData = undefined;
const contentType = request.headers.get("Content-Type");
if (contentType && contentType.indexOf("multipart/form-data") !== -1) {
// browser will automatically apply a suitable content-type header
request.headers.remove("Content-Type");
}
}
xhr.open(request.method, request.url);
xhr.timeout = request.timeout;
xhr.withCredentials = request.withCredentials;
for (const header of request.headers.headersArray()) {
xhr.setRequestHeader(header.name, header.value);
}
xhr.responseType = request.streamResponseBody ? "blob" : "text";
// tslint:disable-next-line:no-null-keyword
xhr.send(request.body === undefined ? null : request.body);
if (request.streamResponseBody) {
return new Promise((resolve, reject) => {
xhr.addEventListener("readystatechange", () => {
// Resolve as soon as headers are loaded
if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
const blobBody = new Promise<Blob>((resolve, reject) => {
xhr.addEventListener("load", () => {
resolve(xhr.response);
});
rejectOnTerminalEvent(request, xhr, reject);
});
resolve({
request,
status: xhr.status,
headers: parseHeaders(xhr),
blobBody,
});
}
});
rejectOnTerminalEvent(request, xhr, reject);
});
} else {
return new Promise(function (resolve, reject) {
xhr.addEventListener("load", () =>
resolve({
request,
status: xhr.status,
headers: parseHeaders(xhr),
bodyAsText: xhr.responseText,
})
);
rejectOnTerminalEvent(request, xhr, reject);
});
}
}
}
function addProgressListener(
xhr: XMLHttpRequestEventTarget,
listener?: (progress: TransferProgressEvent) => void
) {
if (listener) {
xhr.addEventListener("progress", (rawEvent) =>
listener({
loadedBytes: rawEvent.loaded,
})
);
}
}
// exported locally for testing
export function parseHeaders(xhr: XMLHttpRequest) {
const responseHeaders = new HttpHeaders();
const headerLines = xhr
.getAllResponseHeaders()
.trim()
.split(/[\r\n]+/);
for (const line of headerLines) {
const index = line.indexOf(":");
const headerName = line.slice(0, index);
const headerValue = line.slice(index + 2);
responseHeaders.set(headerName, headerValue);
}
return responseHeaders;
}
function rejectOnTerminalEvent(
request: WebResourceLike,
xhr: XMLHttpRequest,
reject: (err: any) => void
) {
xhr.addEventListener("error", () =>
reject(
new RestError(
`Failed to send request to ${request.url}`,
RestError.REQUEST_SEND_ERROR,
undefined,
request
)
)
);
xhr.addEventListener("abort", () =>
reject(
new RestError("The request was aborted", RestError.REQUEST_ABORTED_ERROR, undefined, request)
)
);
xhr.addEventListener("timeout", () =>
reject(
new RestError(
`timeout of ${xhr.timeout}ms exceeded`,
RestError.REQUEST_SEND_ERROR,
undefined,
request
)
)
);
}