Merge remote-tracking branch 'upstream/master' into global-fetch-port-pr9880

This commit is contained in:
Jeremy Meng 2021-02-02 19:40:13 +00:00
Родитель 763f66cda6 ba4c46001f
Коммит a35708407d
86 изменённых файлов: 3826 добавлений и 2043 удалений

2
.github/CODEOWNERS поставляемый
Просмотреть файл

@ -1,2 +1,2 @@
# Default owners
* @daviwil @ramya-rao-a @sergey-shandar
* @ramya-rao-a @jeremymeng @xirzec

2
.prettierignore Normal file
Просмотреть файл

@ -0,0 +1,2 @@
typings/
**/*.d.ts

9
.prettierrc.json Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"printWidth": 100,
"semi": true,
"singleQuote": false,
"tabWidth": 2
}

Просмотреть файл

@ -1,4 +1,11 @@
import { findPackageJsonFileSync, PackageJson, readPackageJsonFileSync, getParentFolderPath, joinPath, fileExistsSync } from "@ts-common/azure-js-dev-tools";
import {
findPackageJsonFileSync,
PackageJson,
readPackageJsonFileSync,
getParentFolderPath,
joinPath,
fileExistsSync,
} from "@ts-common/azure-js-dev-tools";
import { readFileSync } from "fs";
import { Logger, getDefaultLogger } from "@azure/logger-js";
@ -21,20 +28,29 @@ export function checkConstantsVersion(): number {
error(`Could not find a version property in ${packageJsonFilePath}.`);
} else {
const repositoryRootFolderPath: string = getParentFolderPath(packageJsonFilePath);
const constantsTsFilePath: string = joinPath(repositoryRootFolderPath, "lib/util/constants.ts");
const constantsTsFilePath: string = joinPath(
repositoryRootFolderPath,
"lib/util/constants.ts"
);
if (!fileExistsSync(constantsTsFilePath)) {
error(`${constantsTsFilePath} doesn't exist anymore. Where'd it go?`);
} else {
const constantsTsFileContents: string = readFileSync(constantsTsFilePath, { encoding: "utf8" });
const constantsTsFileContents: string = readFileSync(constantsTsFilePath, {
encoding: "utf8",
});
const regularExpressionString = `msRestVersion: "(.*)"`;
const regularExpression = new RegExp(regularExpressionString);
const match: RegExpMatchArray | null = constantsTsFileContents.match(regularExpression);
if (!match) {
error(`${constantsTsFilePath} doesn't contain a match for ${regularExpressionString}.`);
} else if (match[1] !== packageVersion) {
error(`Expected ${constantsTsFilePath} to contain an msRestVersion property with the value "${packageVersion}", but it was "${match[1]}" instead.`);
error(
`Expected ${constantsTsFilePath} to contain an msRestVersion property with the value "${packageVersion}", but it was "${match[1]}" instead.`
);
} else {
logger.logInfo(`${constantsTsFilePath} contained the correct value for msRestVersion ("${packageVersion}").`);
logger.logInfo(
`${constantsTsFilePath} contained the correct value for msRestVersion ("${packageVersion}").`
);
}
}
}

Просмотреть файл

@ -4,6 +4,6 @@ import { checkConstantsVersion } from "./checkConstantsVersion";
checkEverything({
additionalChecks: {
name: "Constants.ts Version",
check: checkConstantsVersion
}
check: checkConstantsVersion,
},
});

Просмотреть файл

@ -1,3 +1,3 @@
import { checkForOnlyCalls } from "@ts-common/azure-js-dev-tools";
checkForOnlyCalls();
checkForOnlyCalls();

Просмотреть файл

@ -1,3 +1,3 @@
import { checkPackageJsonVersion } from "@ts-common/azure-js-dev-tools";
checkPackageJsonVersion();
checkPackageJsonVersion();

Просмотреть файл

@ -1,3 +1,3 @@
import { changeClonedDependenciesTo } from "@ts-common/azure-js-dev-tools";
changeClonedDependenciesTo(__dirname, "latest");
changeClonedDependenciesTo(__dirname, "latest");

Просмотреть файл

@ -1,3 +1,3 @@
import { changeClonedDependenciesTo } from "@ts-common/azure-js-dev-tools";
changeClonedDependenciesTo(__dirname, "local");
changeClonedDependenciesTo(__dirname, "local");

Просмотреть файл

@ -1,5 +1,11 @@
import path from "path";
import { run, RunResult, RunOptions, Command, commandToString } from "@ts-common/azure-js-dev-tools";
import {
run,
RunResult,
RunOptions,
Command,
commandToString,
} from "@ts-common/azure-js-dev-tools";
async function execAndLog(executable: string, args?: string[], options?: RunOptions): Promise<any> {
const command: Command = {
@ -16,7 +22,11 @@ async function execAndLog(executable: string, args?: string[], options?: RunOpti
showResult: true,
});
console.log(`\nResult of "${commandToString(command)}" [Exit code: ${result.exitCode}]:\n` + result.stdout + "\n");
console.log(
`\nResult of "${commandToString(command)}" [Exit code: ${result.exitCode}]:\n` +
result.stdout +
"\n"
);
if (result.exitCode) {
console.error(`Error while running "${commandToString(command)}": ${result.error}`);
@ -29,19 +39,21 @@ async function execAndLog(executable: string, args?: string[], options?: RunOpti
async function cloneRepository(projectName: string, projectDirectory: string) {
const gitHubUrl = `https://github.com/Azure/${projectName}.git`;
await execAndLog(`git`, ["clone", gitHubUrl, projectDirectory, "--recursive"]);
await execAndLog(`npm`, [ "install" ], { executionFolderPath: projectDirectory });
await execAndLog(`npm`, ["install"], { executionFolderPath: projectDirectory });
}
async function buildAndTest(projectDirectory: string) {
await execAndLog(`npm`, [ "run", "build" ], { executionFolderPath: projectDirectory });
await execAndLog(`npm`, [ "run", "test" ], { executionFolderPath: projectDirectory });
await execAndLog(`npm`, ["run", "build"], { executionFolderPath: projectDirectory });
await execAndLog(`npm`, ["run", "test"], { executionFolderPath: projectDirectory });
}
async function cloneAndRunTest(msRestJsDirectory: string, projectName: string) {
const projectDirectory = path.join(msRestJsDirectory, `../.tmp/${projectName}`);
await cloneRepository(projectName, projectDirectory);
await execAndLog(`npm`, [ "install", msRestJsDirectory ], { executionFolderPath: projectDirectory });
await execAndLog(`npm`, ["install", msRestJsDirectory], {
executionFolderPath: projectDirectory,
});
const additionalCommands: string[] = process.argv.slice(3);
for (const command of additionalCommands) {
@ -49,7 +61,7 @@ async function cloneAndRunTest(msRestJsDirectory: string, projectName: string) {
}
await buildAndTest(projectDirectory);
await execAndLog(`rm`, [ "-rf", projectDirectory ]);
await execAndLog(`rm`, ["-rf", projectDirectory]);
}
(async () => {

Просмотреть файл

@ -1,9 +1,13 @@
# Changelog
## 2.2.1 - (Unreleased)
## 2.2.2 - 2021-02-02
- The global `fetch()` is no longer overridden by node-fetch. Fixes (Issue [#383](https://github.com/Azure/ms-rest-js/issues/383))
## 2.2.1 - 2021-02-02
- Fix issue of `SystemErrorRetryPolicy` didn't retry on errors (Issue [#412](https://github.com/Azure/ms-rest-js/issues/412))
## 2.2.0 - 2021-01-26
- Add support for @azure/core-auth's TokenCredential (PR [#410](https://github.com/Azure/ms-rest-js/pull/410))
- Allow = character in parameter value (PR [#408](https://github.com/Azure/ms-rest-js/pull/408))

Просмотреть файл

@ -5,20 +5,21 @@
Runtime for isomorphic javascript libraries (that work in the browser and node.js environment) generated via [Autorest](https://github.com/Azure/Autorest).
## Requirements
- node.js version > 6.x
- npm install -g typescript
- Node.js version > 6.x
- `npm install -g typescript`
## Installation
- After cloning the repo, execute `npm install`
## Execution
### node.js
- Set the subscriptionId and token
- Run `node samples/node-sample.js`
### Node.js
- Set the subscriptionId and token as instructed in `samples/node-samples.ts`
- Run `npx ts-node samples/node-sample.js`
### In the browser
- Set the subscriptionId and token and then run
- Run `npm run build`
- Set the subscriptionId and token then
- Open index.html file in the browser. It should show the response from GET request on the storage account. From Chrome type Ctrl + Shift + I and you can see the logs in console.
## Architecture Overview

Просмотреть файл

@ -1,14 +1,10 @@
const defaults = {
port: 9876
port: 9876,
};
module.exports = function (config: any) {
config.set({
plugins: [
"karma-mocha",
"karma-chrome-launcher",
"karma-firefox-launcher"
],
plugins: ["karma-mocha", "karma-chrome-launcher", "karma-firefox-launcher"],
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
@ -19,7 +15,7 @@ module.exports = function (config: any) {
{ pattern: "dist/msRest.browser.js" },
{ pattern: "dist/msRest.browser.js.map", included: false },
{ pattern: "test/msRest.browser.test.js" },
{ pattern: "test/msRest.browser.test.js.map", included: false }
{ pattern: "test/msRest.browser.test.js.map", included: false },
],
// test results reporter to use
@ -52,16 +48,20 @@ module.exports = function (config: any) {
customLaunchers: {
ChromeNoSecurity: {
base: "ChromeHeadless",
flags: ["--disable-web-security"]
flags: ["--disable-web-security"],
},
ChromeDebugging: {
base: "Chrome",
flags: [`http://localhost:${defaults.port}/debug.html`, "--auto-open-devtools-for-tabs", "--disable-web-security"]
flags: [
`http://localhost:${defaults.port}/debug.html`,
"--auto-open-devtools-for-tabs",
"--disable-web-security",
],
},
FirefoxDebugging: {
base: "Firefox",
flags: ["-url", `http://localhost:${defaults.port}/debug.html`, "-devtools"]
}
flags: ["-url", `http://localhost:${defaults.port}/debug.html`, "-devtools"],
},
},
});
};

Просмотреть файл

@ -1,7 +1,12 @@
// 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 {
CommonRequestInfo,
CommonRequestInit,
CommonResponse,
FetchHttpClient,
} from "./fetchHttpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResourceLike } from "./webResource";

Просмотреть файл

@ -39,7 +39,9 @@ export class ApiKeyCredentials implements ServiceClientCredentials {
*/
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.`);
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;
@ -53,7 +55,9 @@ export class ApiKeyCredentials implements ServiceClientCredentials {
*/
signRequest(webResource: WebResourceLike): Promise<WebResourceLike> {
if (!webResource) {
return Promise.reject(new Error(`webResource cannot be null or undefined and must be of type "object".`));
return Promise.reject(
new Error(`webResource cannot be null or undefined and must be of type "object".`)
);
}
if (this.inHeader) {
@ -82,4 +86,4 @@ export class ApiKeyCredentials implements ServiceClientCredentials {
return Promise.resolve(webResource);
}
}
}

Просмотреть файл

@ -14,8 +14,7 @@ const DEFAULT_AUTHORIZATION_SCHEME = "Bearer";
* 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 {
export class AzureIdentityCredentialAdapter implements ServiceClientCredentials {
private azureTokenCredential: TokenCredential;
private scopes: string | string[];
constructor(

Просмотреть файл

@ -22,7 +22,11 @@ export class BasicAuthenticationCredentials implements ServiceClientCredentials
* @param {string} password Password.
* @param {string} [authorizationScheme] The authorization scheme.
*/
constructor(userName: string, password: string, authorizationScheme: string = DEFAULT_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.");
}

Просмотреть файл

@ -4,20 +4,20 @@
import { ApiKeyCredentials, ApiKeyCredentialOptions } from "./apiKeyCredentials";
export class DomainCredentials extends ApiKeyCredentials {
/**
* Creates a new EventGrid DomainCredentials object.
*
* @constructor
* @param {string} domainKey The EventGrid domain key
*/
/**
* 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
}
"aeg-sas-key": domainKey,
},
};
super(options);
}

Просмотреть файл

@ -39,7 +39,10 @@ export class TokenCredentials implements ServiceClientCredentials {
*/
signRequest(webResource: WebResourceLike) {
if (!webResource.headers) webResource.headers = new HttpHeaders();
webResource.headers.set(HeaderConstants.AUTHORIZATION, `${this.authorizationScheme} ${this.token}`);
webResource.headers.set(
HeaderConstants.AUTHORIZATION,
`${this.authorizationScheme} ${this.token}`
);
return Promise.resolve(webResource);
}
}

Просмотреть файл

@ -9,4 +9,4 @@ export interface TokenResponse {
readonly tokenType: string;
readonly accessToken: string;
readonly [x: string]: any;
}
}

Просмотреть файл

@ -4,20 +4,20 @@
import { ApiKeyCredentials, ApiKeyCredentialOptions } from "./apiKeyCredentials";
export class TopicCredentials extends ApiKeyCredentials {
/**
* Creates a new EventGrid TopicCredentials object.
*
* @constructor
* @param {string} topicKey The EventGrid topic key
*/
/**
* 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
}
"aeg-sas-key": topicKey,
},
};
super(options);
}

Просмотреть файл

@ -34,13 +34,20 @@ export type CommonResponse = Omit<Response, "body" | "trailer" | "formData"> & {
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.");
throw new Error(
"'httpRequest' (WebResource) cannot be null or undefined and must be of type object."
);
}
const abortController = new AbortController();
if (httpRequest.abortSignal) {
if (httpRequest.abortSignal.aborted) {
throw new RestError("The request was aborted", RestError.REQUEST_ABORTED_ERROR, undefined, httpRequest);
throw new RestError(
"The request was aborted",
RestError.REQUEST_ABORTED_ERROR,
undefined,
httpRequest
);
}
httpRequest.abortSignal.addEventListener("abort", (event: Event) => {
@ -60,7 +67,7 @@ export abstract class FetchHttpClient implements HttpClient {
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
// value function probably returns a stream so we can provide a fresh stream on each retry
if (typeof value === "function") {
value = value();
}
@ -86,7 +93,10 @@ export abstract class FetchHttpClient implements HttpClient {
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()}`);
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");
@ -95,8 +105,10 @@ export abstract class FetchHttpClient implements HttpClient {
}
let body = httpRequest.body
? (typeof httpRequest.body === "function" ? httpRequest.body() : httpRequest.body)
: undefined;
? typeof httpRequest.body === "function"
? httpRequest.body()
: httpRequest.body
: undefined;
if (httpRequest.onUploadProgress && httpRequest.body) {
let loadedBytes = 0;
const uploadReportStream = new Transform({
@ -104,7 +116,7 @@ export abstract class FetchHttpClient implements HttpClient {
loadedBytes += chunk.length;
httpRequest.onUploadProgress!({ loadedBytes });
callback(undefined, chunk);
}
},
});
if (isReadableStream(body)) {
@ -116,14 +128,16 @@ export abstract class FetchHttpClient implements HttpClient {
body = uploadReportStream;
}
const platformSpecificRequestInit: Partial<RequestInit> = await this.prepareRequest(httpRequest);
const platformSpecificRequestInit: Partial<RequestInit> = await this.prepareRequest(
httpRequest
);
const requestInit: RequestInit = {
body: body,
headers: httpRequest.headers.rawHeaders(),
method: httpRequest.method,
signal: abortController.signal,
...platformSpecificRequestInit
...platformSpecificRequestInit,
};
try {
@ -134,12 +148,14 @@ export abstract class FetchHttpClient implements HttpClient {
headers: headers,
request: httpRequest,
status: response.status,
readableStreamBody: httpRequest.streamResponseBody ? (response.body as unknown) as NodeJS.ReadableStream : undefined,
readableStreamBody: httpRequest.streamResponseBody
? ((response.body as unknown) as NodeJS.ReadableStream)
: undefined,
bodyAsText: !httpRequest.streamResponseBody ? await response.text() : undefined,
};
const onDownloadProgress = httpRequest.onDownloadProgress;
if (onDownloadProgress) {
if (onDownloadProgress) {
const responseBody: ReadableStream<Uint8Array> | undefined = response.body || undefined;
if (isReadableStream(responseBody)) {
@ -149,7 +165,7 @@ export abstract class FetchHttpClient implements HttpClient {
loadedBytes += chunk.length;
onDownloadProgress({ loadedBytes });
callback(undefined, chunk);
}
},
});
responseBody.pipe(downloadReportStream);
operationResponse.readableStreamBody = downloadReportStream;
@ -168,9 +184,19 @@ export abstract class FetchHttpClient implements HttpClient {
} catch (error) {
const fetchError: FetchError = error;
if (fetchError.code === "ENOTFOUND") {
throw new RestError(fetchError.message, RestError.REQUEST_SEND_ERROR, undefined, httpRequest);
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 new RestError(
"The request was aborted",
RestError.REQUEST_ABORTED_ERROR,
undefined,
httpRequest
);
}
throw fetchError;

Просмотреть файл

@ -6,5 +6,4 @@ import { RequestPolicy } from "./policies/requestPolicy";
/**
* An interface that can send HttpRequests and receive promised HttpResponses.
*/
export interface HttpClient extends RequestPolicy {
}
export interface HttpClient extends RequestPolicy {}

Просмотреть файл

@ -126,7 +126,10 @@ export class HttpHeaders {
* @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() };
this._headersMap[getHeaderKey(headerName)] = {
name: headerName,
value: headerValue.toString(),
};
}
/**

Просмотреть файл

@ -23,5 +23,5 @@ export enum HttpPipelineLogLevel {
/**
* An information log.
*/
INFO
}
INFO,
}

Просмотреть файл

@ -29,8 +29,7 @@ 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) {
}
constructor(public minimumLogLevel: HttpPipelineLogLevel) {}
/**
* Log the provided message.
@ -40,17 +39,17 @@ export class ConsoleHttpPipelineLogger implements HttpPipelineLogger {
log(logLevel: HttpPipelineLogLevel, message: string): void {
const logMessage = `${HttpPipelineLogLevel[logLevel]}: ${message}`;
switch (logLevel) {
case HttpPipelineLogLevel.ERROR:
console.error(logMessage);
break;
case HttpPipelineLogLevel.ERROR:
console.error(logMessage);
break;
case HttpPipelineLogLevel.WARNING:
console.warn(logMessage);
break;
case HttpPipelineLogLevel.WARNING:
console.warn(logMessage);
break;
case HttpPipelineLogLevel.INFO:
console.log(logMessage);
break;
case HttpPipelineLogLevel.INFO:
console.log(logMessage);
break;
}
}
}
}

Просмотреть файл

@ -1,7 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
export { WebResource, WebResourceLike, HttpRequestBody, RequestPrepareOptions, HttpMethods, ParameterValue, RequestOptionsBase, TransferProgressEvent, AbortSignalLike } from "./webResource";
export {
WebResource,
WebResourceLike,
HttpRequestBody,
RequestPrepareOptions,
HttpMethods,
ParameterValue,
RequestOptionsBase,
TransferProgressEvent,
AbortSignalLike,
} from "./webResource";
export { DefaultHttpClient } from "./defaultHttpClient";
export { HttpClient } from "./httpClient";
export { HttpHeaders, HttpHeadersLike } from "./httpHeaders";
@ -10,14 +20,24 @@ export { HttpPipelineLogger } from "./httpPipelineLogger";
export { HttpPipelineLogLevel } from "./httpPipelineLogLevel";
export { RestError } from "./restError";
export { OperationArguments } from "./operationArguments";
export { OperationParameter, OperationQueryParameter, OperationURLParameter } from "./operationParameter";
export {
OperationParameter,
OperationQueryParameter,
OperationURLParameter,
} from "./operationParameter";
export { OperationResponse } from "./operationResponse";
export { OperationSpec } from "./operationSpec";
export { 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptions,
RequestPolicyOptionsLike,
} from "./policies/requestPolicy";
export { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
export { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
export { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
@ -29,16 +49,38 @@ export { signingPolicy } from "./policies/signingPolicy";
export { userAgentPolicy, getDefaultUserAgentValue } from "./policies/userAgentPolicy";
export { deserializationPolicy, deserializeResponseBody } from "./policies/deserializationPolicy";
export {
MapperType, SimpleMapperType, CompositeMapperType, DictionaryMapperType, SequenceMapperType, EnumMapperType,
Mapper, BaseMapper, CompositeMapper, SequenceMapper, DictionaryMapper, EnumMapper,
MapperConstraints, PolymorphicDiscriminator,
Serializer, UrlParameterValue, serializeObject
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
stripRequest,
stripResponse,
delay,
executePromisesSequentially,
generateUuid,
encodeUri,
ServiceCallback,
promiseToCallback,
promiseToServiceCallback,
isValidUuid,
applyMixins,
isNode,
isDuration,
} from "./util/utils";
export { URLBuilder, URLQuery } from "./url";

Просмотреть файл

@ -6,7 +6,12 @@ import * as http from "http";
import * as https from "https";
import node_fetch from "node-fetch";
import { CommonRequestInfo, CommonRequestInit, CommonResponse, FetchHttpClient } from "./fetchHttpClient";
import {
CommonRequestInfo,
CommonRequestInit,
CommonResponse,
FetchHttpClient,
} from "./fetchHttpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResourceLike } from "./webResource";
import { createProxyAgent, ProxyAgent } from "./proxyAgent";
@ -15,7 +20,7 @@ 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>;
return (node_fetch(input, init) as unknown) as Promise<CommonResponse>;
}
async prepareRequest(httpRequest: WebResourceLike): Promise<Partial<RequestInit>> {
@ -36,14 +41,18 @@ export class NodeFetchHttpClient extends FetchHttpClient {
}
if (httpRequest.agentSettings) {
const {http: httpAgent, https: httpsAgent} = 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);
const tunnel: ProxyAgent = createProxyAgent(
httpRequest.url,
httpRequest.proxySettings,
httpRequest.headers
);
requestInit.agent = tunnel.agent;
}
@ -52,7 +61,9 @@ export class NodeFetchHttpClient extends FetchHttpClient {
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);
const agent = httpRequest.url.startsWith("https")
? new https.Agent(options)
: new http.Agent(options);
requestInit.agent = agent;
}
}
@ -69,13 +80,14 @@ export class NodeFetchHttpClient extends FetchHttpClient {
setCookieHeader,
operationResponse.request.url,
{ ignoreError: true },
err => {
(err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
}
);
});
}
}

Просмотреть файл

@ -16,4 +16,4 @@ export interface OperationArguments {
* The optional arugments that are provided to an operation.
*/
options?: RequestOptionsBase;
}
}

Просмотреть файл

@ -58,7 +58,10 @@ export function getPathStringFromParameter(parameter: OperationParameter): strin
return getPathStringFromParameterPath(parameter.parameterPath, parameter.mapper);
}
export function getPathStringFromParameterPath(parameterPath: ParameterPath, mapper: Mapper): string {
export function getPathStringFromParameterPath(
parameterPath: ParameterPath,
mapper: Mapper
): string {
let result: string;
if (typeof parameterPath === "string") {
result = parameterPath;
@ -68,4 +71,4 @@ export function getPathStringFromParameterPath(parameterPath: ParameterPath, map
result = mapper.serializedName!;
}
return result;
}
}

Просмотреть файл

@ -16,4 +16,4 @@ export interface OperationResponse {
* The mapper that will be used to deserialize the response body.
*/
bodyMapper?: Mapper;
}
}

Просмотреть файл

@ -1,7 +1,11 @@
// 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 {
OperationParameter,
OperationQueryParameter,
OperationURLParameter,
} from "./operationParameter";
import { OperationResponse } from "./operationResponse";
import { MapperType, Serializer } from "./serializer";
import { HttpMethods } from "./webResource";
@ -82,10 +86,13 @@ 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) {
if (
operationResponse.bodyMapper &&
operationResponse.bodyMapper.type.name === MapperType.Stream
) {
result = true;
break;
}
}
return result;
}
}

Просмотреть файл

@ -2,7 +2,12 @@
// 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
@ -12,7 +17,7 @@ export function agentPolicy(_agentSettings?: AgentSettings): RequestPolicyFactor
return {
create: (_nextPolicy: RequestPolicy, _options: RequestPolicyOptionsLike) => {
throw agentNotSupportedInBrowser;
}
},
};
}

Просмотреть файл

@ -2,7 +2,12 @@
// 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
@ -10,14 +15,18 @@ 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) {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
agentSettings: AgentSettings
) {
super(nextPolicy, options);
this.agentSettings = agentSettings;
}

Просмотреть файл

@ -9,7 +9,12 @@ 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";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
/**
* The content-types that will indicate that an operation response should be deserialized in a
@ -33,11 +38,13 @@ export interface DeserializationContentTypes {
* Create a new serialization RequestPolicyCreator that will serialized HTTP request bodies as they
* pass through the HTTP pipeline.
*/
export function deserializationPolicy(deserializationContentTypes?: DeserializationContentTypes): RequestPolicyFactory {
export function deserializationPolicy(
deserializationContentTypes?: DeserializationContentTypes
): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new DeserializationPolicy(nextPolicy, deserializationContentTypes, options);
}
},
};
}
@ -52,24 +59,41 @@ export class DeserializationPolicy extends BaseRequestPolicy {
public readonly jsonContentTypes: string[];
public readonly xmlContentTypes: string[];
constructor(nextPolicy: RequestPolicy, deserializationContentTypes: DeserializationContentTypes | undefined, options: RequestPolicyOptionsLike) {
constructor(
nextPolicy: RequestPolicy,
deserializationContentTypes: DeserializationContentTypes | undefined,
options: RequestPolicyOptionsLike
) {
super(nextPolicy, options);
this.jsonContentTypes = deserializationContentTypes && deserializationContentTypes.json || defaultJsonContentTypes;
this.xmlContentTypes = deserializationContentTypes && deserializationContentTypes.xml || defaultXmlContentTypes;
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));
return this._nextPolicy
.sendRequest(request)
.then((response: HttpOperationResponse) =>
deserializeResponseBody(this.jsonContentTypes, this.xmlContentTypes, response)
);
}
}
function getOperationResponse(parsedResponse: HttpOperationResponse): undefined | OperationResponse {
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;
const operationResponseGetter:
| undefined
| ((
operationSpec: OperationSpec,
response: HttpOperationResponse
) => undefined | OperationResponse) = request.operationResponseGetter;
if (!operationResponseGetter) {
result = operationSpec.responses[parsedResponse.status];
} else {
@ -80,7 +104,8 @@ function getOperationResponse(parsedResponse: HttpOperationResponse): undefined
}
function shouldDeserializeResponse(parsedResponse: HttpOperationResponse): boolean {
const shouldDeserialize: undefined | boolean | ((response: HttpOperationResponse) => boolean) = parsedResponse.request.shouldDeserialize;
const shouldDeserialize: undefined | boolean | ((response: HttpOperationResponse) => boolean) =
parsedResponse.request.shouldDeserialize;
let result: boolean;
if (shouldDeserialize === undefined) {
result = true;
@ -92,8 +117,12 @@ function shouldDeserializeResponse(parsedResponse: HttpOperationResponse): boole
return result;
}
export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTypes: string[], response: HttpOperationResponse): Promise<HttpOperationResponse> {
return parse(jsonContentTypes, xmlContentTypes, response).then(parsedResponse => {
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;
@ -102,17 +131,21 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
const expectedStatusCodes: string[] = Object.keys(operationSpec.responses);
const hasNoExpectedStatusCodes: boolean = (expectedStatusCodes.length === 0 || (expectedStatusCodes.length === 1 && expectedStatusCodes[0] === "default"));
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;
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;
: (parsedResponse.bodyAsText as string);
const error = new RestError(initialErrorMessage);
error.statusCode = statusCode;
@ -122,8 +155,12 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
let parsedErrorResponse: { [key: string]: any } = parsedResponse.parsedBody;
try {
if (parsedErrorResponse) {
const defaultResponseBodyMapper: Mapper | undefined = defaultResponseSpec.bodyMapper;
if (defaultResponseBodyMapper && defaultResponseBodyMapper.serializedName === "CloudError") {
const defaultResponseBodyMapper: Mapper | undefined =
defaultResponseSpec.bodyMapper;
if (
defaultResponseBodyMapper &&
defaultResponseBodyMapper.serializedName === "CloudError"
) {
if (parsedErrorResponse.error) {
parsedErrorResponse = parsedErrorResponse.error;
}
@ -147,12 +184,20 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
if (defaultResponseBodyMapper) {
let valueToDeserialize: any = parsedErrorResponse;
if (operationSpec.isXML && defaultResponseBodyMapper.type.name === MapperType.Sequence) {
valueToDeserialize = typeof parsedErrorResponse === "object"
? parsedErrorResponse[defaultResponseBodyMapper.xmlElementName!]
: [];
if (
operationSpec.isXML &&
defaultResponseBodyMapper.type.name === MapperType.Sequence
) {
valueToDeserialize =
typeof parsedErrorResponse === "object"
? parsedErrorResponse[defaultResponseBodyMapper.xmlElementName!]
: [];
}
error.body = operationSpec.serializer.deserialize(defaultResponseBodyMapper, valueToDeserialize, "error.body");
error.body = operationSpec.serializer.deserialize(
defaultResponseBodyMapper,
valueToDeserialize,
"error.body"
);
}
}
} catch (defaultError) {
@ -164,12 +209,21 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
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!] : [];
valueToDeserialize =
typeof valueToDeserialize === "object"
? valueToDeserialize[responseSpec.bodyMapper.xmlElementName!]
: [];
}
try {
parsedResponse.parsedBody = operationSpec.serializer.deserialize(responseSpec.bodyMapper, valueToDeserialize, "operationRes.parsedBody");
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}`);
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);
@ -180,7 +234,11 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
}
if (responseSpec.headersMapper) {
parsedResponse.parsedHeaders = operationSpec.serializer.deserialize(responseSpec.headersMapper, parsedResponse.headers.rawHeaders(), "operationRes.parsedHeaders");
parsedResponse.parsedHeaders = operationSpec.serializer.deserialize(
responseSpec.headersMapper,
parsedResponse.headers.rawHeaders(),
"operationRes.parsedHeaders"
);
}
}
}
@ -189,26 +247,42 @@ export function deserializeResponseBody(jsonContentTypes: string[], xmlContentTy
});
}
function parse(jsonContentTypes: string[], xmlContentTypes: string[], operationResponse: HttpOperationResponse): Promise<HttpOperationResponse> {
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);
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 => {
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)) {
} else if (contentComponents.some((component) => xmlContentTypes.indexOf(component) !== -1)) {
return parseXML(text)
.then(body => {
.then((body) => {
operationResponse.parsedBody = body;
return operationResponse;
})
@ -217,4 +291,4 @@ function parse(jsonContentTypes: string[], xmlContentTypes: string[], operationR
}
return Promise.resolve(operationResponse);
}
}

Просмотреть файл

@ -4,7 +4,12 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { RestError } from "../restError";
export interface RetryData {
@ -19,11 +24,23 @@ export interface RetryError extends Error {
innerError?: RetryError;
}
export function exponentialRetryPolicy(retryCount?: number, retryInterval?: number, minRetryInterval?: number, maxRetryInterval?: number): RequestPolicyFactory {
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);
}
return new ExponentialRetryPolicy(
nextPolicy,
options,
retryCount,
retryInterval,
minRetryInterval,
maxRetryInterval
);
},
};
}
@ -63,19 +80,33 @@ export class ExponentialRetryPolicy extends BaseRequestPolicy {
* @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) {
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"; }
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;
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));
return this._nextPolicy
.sendRequest(request.clone())
.then((response) => retry(this, request, response))
.catch((error) => retry(this, request, error.response, undefined, error));
}
}
@ -87,8 +118,17 @@ export class ExponentialRetryPolicy extends BaseRequestPolicy {
* @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) {
function shouldRetry(
policy: ExponentialRetryPolicy,
statusCode: number | undefined,
retryData: RetryData
): boolean {
if (
statusCode == undefined ||
(statusCode < 500 && statusCode !== 408) ||
statusCode === 501 ||
statusCode === 505
) {
return false;
}
@ -96,10 +136,10 @@ function shouldRetry(policy: ExponentialRetryPolicy, statusCode: number | undefi
if (!retryData) {
throw new Error("retryData for the ExponentialRetryPolicyFilter cannot be null.");
} else {
currentCount = (retryData && retryData.retryCount);
currentCount = retryData && retryData.retryCount;
}
return (currentCount < policy.retryCount);
return currentCount < policy.retryCount;
}
/**
@ -109,11 +149,15 @@ function shouldRetry(policy: ExponentialRetryPolicy, statusCode: number | undefi
* @param {RetryData} retryData The retry data.
* @param {RetryError} [err] The operation"s error, if any.
*/
function updateRetryData(policy: ExponentialRetryPolicy, retryData?: RetryData, err?: RetryError): RetryData {
function updateRetryData(
policy: ExponentialRetryPolicy,
retryData?: RetryData,
err?: RetryError
): RetryData {
if (!retryData) {
retryData = {
retryCount: 0,
retryInterval: 0
retryInterval: 0,
};
}
@ -130,34 +174,47 @@ function updateRetryData(policy: ExponentialRetryPolicy, retryData?: RetryData,
// Adjust retry interval
let incrementDelta = Math.pow(2, retryData.retryCount) - 1;
const boundedRandDelta = policy.retryInterval * 0.8 +
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);
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> {
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)
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));
.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 ||
const err =
retryData.error ||
new RestError(
"Failed to send the request.",
RestError.REQUEST_SEND_ERROR,
response && response.status,
response && response.request,
response);
response
);
return Promise.reject(err);
} else {
return Promise.resolve(response);
}
}
}

Просмотреть файл

@ -4,18 +4,29 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function generateClientRequestIdPolicy(requestIdHeaderName = "x-ms-client-request-id"): RequestPolicyFactory {
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) {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
private _requestIdHeaderName: string
) {
super(nextPolicy, options);
}

Просмотреть файл

@ -3,33 +3,45 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
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) {
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));
return this._nextPolicy.sendRequest(request).then((response) => logResponse(this, response));
}
}
function logResponse(policy: LogPolicy, response: HttpOperationResponse): Promise<HttpOperationResponse> {
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);
}
}

Просмотреть файл

@ -21,7 +21,7 @@ export function getPlatformSpecificData(): TelemetryInfo[] {
const navigator = window.navigator as NavigatorEx;
const osInfo = {
key: "OS",
value: (navigator.oscpu || navigator.platform).replace(" ", "")
value: (navigator.oscpu || navigator.platform).replace(" ", ""),
};
return [osInfo];

Просмотреть файл

@ -12,12 +12,12 @@ export function getDefaultUserAgentKey(): string {
export function getPlatformSpecificData(): TelemetryInfo[] {
const runtimeInfo = {
key: "Node",
value: process.version
value: process.version,
};
const osInfo = {
key: "OS",
value: `(${os.arch()}-${os.type()}-${os.release()})`
value: `(${os.arch()}-${os.type()}-${os.release()})`,
};
return [runtimeInfo, osInfo];

Просмотреть файл

@ -2,7 +2,12 @@
// 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
@ -16,7 +21,7 @@ export function proxyPolicy(_proxySettings?: ProxySettings): RequestPolicyFactor
return {
create: (_nextPolicy: RequestPolicy, _options: RequestPolicyOptionsLike) => {
throw proxyNotSupportedInBrowser;
}
},
};
}

Просмотреть файл

@ -1,7 +1,12 @@
// 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
import { HttpOperationResponse } from "../httpOperationResponse";
import { ProxySettings } from "../serviceClient";
import { WebResourceLike } from "../webResource";
@ -37,23 +42,26 @@ export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | unde
const parsedUrl = URLBuilder.parse(proxyUrl);
return {
host: parsedUrl.getScheme() + "://" + parsedUrl.getHost(),
port: Number.parseInt(parsedUrl.getPort() || "80")
port: Number.parseInt(parsedUrl.getPort() || "80"),
};
}
export function proxyPolicy(proxySettings?: ProxySettings): RequestPolicyFactory {
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) {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
proxySettings: ProxySettings
) {
super(nextPolicy, options);
this.proxySettings = proxySettings;
}

Просмотреть файл

@ -4,33 +4,49 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import { URLBuilder } from "../url";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
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) {
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));
return this._nextPolicy
.sendRequest(request)
.then((response) => handleRedirect(this, response, 0));
}
}
function handleRedirect(policy: RedirectPolicy, response: HttpOperationResponse, currentRetries: number): Promise<HttpOperationResponse> {
function handleRedirect(
policy: RedirectPolicy,
response: HttpOperationResponse,
currentRetries: number
): Promise<HttpOperationResponse> {
const { request, status } = response;
const locationHeader = response.headers.get("location");
if (locationHeader &&
if (
locationHeader &&
(status === 300 || status === 307 || (status === 303 && request.method === "POST")) &&
(!policy.maxRetries || currentRetries < policy.maxRetries)) {
(!policy.maxRetries || currentRetries < policy.maxRetries)
) {
const builder = URLBuilder.parse(request.url);
builder.setPath(locationHeader);
request.url = builder.toString();
@ -41,9 +57,10 @@ function handleRedirect(policy: RedirectPolicy, response: HttpOperationResponse,
request.method = "GET";
}
return policy._nextPolicy.sendRequest(request)
.then(res => handleRedirect(policy, res, currentRetries + 1));
return policy._nextPolicy
.sendRequest(request)
.then((res) => handleRedirect(policy, res, currentRetries + 1));
}
return Promise.resolve(response);
}
}

Просмотреть файл

@ -10,7 +10,7 @@ import { WebResourceLike } from "../webResource";
* Creates a new RequestPolicy per-request that uses the provided nextPolicy.
*/
export type RequestPolicyFactory = {
create(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike): RequestPolicy
create(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike): RequestPolicy;
};
export interface RequestPolicy {
@ -18,8 +18,10 @@ export interface RequestPolicy {
}
export abstract class BaseRequestPolicy implements RequestPolicy {
protected constructor(readonly _nextPolicy: RequestPolicy, readonly _options: RequestPolicyOptionsLike) {
}
protected constructor(
readonly _nextPolicy: RequestPolicy,
readonly _options: RequestPolicyOptionsLike
) {}
public abstract sendRequest(webResource: WebResourceLike): Promise<HttpOperationResponse>;
@ -67,8 +69,7 @@ export interface RequestPolicyOptionsLike {
* Optional properties that can be used when creating a RequestPolicy.
*/
export class RequestPolicyOptions implements RequestPolicyOptionsLike {
constructor(private _logger?: HttpPipelineLogger) {
}
constructor(private _logger?: HttpPipelineLogger) {}
/**
* Get whether or not a log with the provided log level should be logged.
@ -76,9 +77,11 @@ export class RequestPolicyOptions implements RequestPolicyOptionsLike {
* @returns Whether or not a log with the provided log level should be logged.
*/
public shouldLog(logLevel: HttpPipelineLogLevel): boolean {
return !!this._logger &&
return (
!!this._logger &&
logLevel !== HttpPipelineLogLevel.OFF &&
logLevel <= this._logger.minimumLogLevel;
logLevel <= this._logger.minimumLogLevel
);
}
/**
@ -92,4 +95,4 @@ export class RequestPolicyOptions implements RequestPolicyOptionsLike {
this._logger.log(logLevel, message);
}
}
}
}

Просмотреть файл

@ -3,47 +3,62 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
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) {
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));
return this._nextPolicy
.sendRequest(request.clone())
.then((response) => registerIfNeeded(this, request, response));
}
}
function registerIfNeeded(policy: RPRegistrationPolicy, request: WebResourceLike, response: HttpOperationResponse): Promise<HttpOperationResponse> {
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 (
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;
})
);
}
}
@ -56,7 +71,10 @@ function registerIfNeeded(policy: RPRegistrationPolicy, request: WebResourceLike
* @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 {
function getRequestEssentials(
originalRequest: WebResourceLike,
reuseUrlToo = false
): WebResourceLike {
const reqOptions: WebResourceLike = originalRequest.clone();
if (reuseUrlToo) {
reqOptions.url = originalRequest.url;
@ -86,8 +104,13 @@ function checkRPNotRegisteredError(body: string): string {
} catch (err) {
// do nothing;
}
if (responseBody && responseBody.error && responseBody.error.message &&
responseBody.error.code && responseBody.error.code === "MissingSubscriptionRegistration") {
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();
@ -105,7 +128,7 @@ function checkRPNotRegisteredError(body: string): string {
*/
function extractSubscriptionUrl(url: string): string {
let result;
const matchRes = url.match(/.*\/subscriptions\/[a-f0-9-]+\//ig);
const matchRes = url.match(/.*\/subscriptions\/[a-f0-9-]+\//gi);
if (matchRes && matchRes[0]) {
result = matchRes[0];
} else {
@ -123,20 +146,24 @@ function extractSubscriptionUrl(url: string): string {
* 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> {
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);
});
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);
});
}
/**
@ -148,17 +175,23 @@ function registerRP(policy: RPRegistrationPolicy, urlPrefix: string, provider: s
* 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> {
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);
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));
return utils
.delay(policy._retryTimeout * 1000)
.then(() => getRegistrationStatus(policy, url, originalRequest));
}
});
}

Просмотреть файл

@ -4,19 +4,29 @@
import { ServiceClientCredentials } from "../credentials/serviceClientCredentials";
import { HttpOperationResponse } from "../httpOperationResponse";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicyFactory, RequestPolicy, RequestPolicyOptionsLike } from "./requestPolicy";
import {
BaseRequestPolicy,
RequestPolicyFactory,
RequestPolicy,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export function signingPolicy(authenticationProvider: ServiceClientCredentials): RequestPolicyFactory {
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) {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
public authenticationProvider: ServiceClientCredentials
) {
super(nextPolicy, options);
}
@ -25,6 +35,8 @@ export class SigningPolicy extends BaseRequestPolicy {
}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
return this.signRequest(request).then(nextRequest => this._nextPolicy.sendRequest(nextRequest));
return this.signRequest(request).then((nextRequest) =>
this._nextPolicy.sendRequest(nextRequest)
);
}
}

Просмотреть файл

@ -4,7 +4,12 @@
import { HttpOperationResponse } from "../httpOperationResponse";
import * as utils from "../util/utils";
import { WebResourceLike } from "../webResource";
import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOptionsLike } from "./requestPolicy";
import {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptionsLike,
} from "./requestPolicy";
export interface RetryData {
retryCount: number;
@ -18,11 +23,23 @@ export interface RetryError extends Error {
innerError?: RetryError;
}
export function systemErrorRetryPolicy(retryCount?: number, retryInterval?: number, minRetryInterval?: number, maxRetryInterval?: number): RequestPolicyFactory {
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);
}
return new SystemErrorRetryPolicy(
nextPolicy,
options,
retryCount,
retryInterval,
minRetryInterval,
maxRetryInterval
);
},
};
}
@ -46,16 +63,32 @@ export class SystemErrorRetryPolicy extends BaseRequestPolicy {
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) {
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;
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()).then(response => retry(this, request, response));
return this._nextPolicy
.sendRequest(request.clone())
.catch((error) => retry(this, request, error.response, error));
}
}
@ -71,9 +104,9 @@ function shouldRetry(policy: SystemErrorRetryPolicy, retryData: RetryData): bool
if (!retryData) {
throw new Error("retryData for the SystemErrorRetryPolicyFilter cannot be null.");
} else {
currentCount = (retryData && retryData.retryCount);
currentCount = retryData && retryData.retryCount;
}
return (currentCount < policy.retryCount);
return currentCount < policy.retryCount;
}
/**
@ -82,11 +115,15 @@ function shouldRetry(policy: SystemErrorRetryPolicy, retryData: RetryData): bool
* @param {RetryData} retryData The retry data.
* @param {object} err The operation"s error, if any.
*/
function updateRetryData(policy: SystemErrorRetryPolicy, retryData?: RetryData, err?: RetryError): RetryData {
function updateRetryData(
policy: SystemErrorRetryPolicy,
retryData?: RetryData,
err?: RetryError
): RetryData {
if (!retryData) {
retryData = {
retryCount: 0,
retryInterval: 0
retryInterval: 0,
};
}
@ -103,31 +140,48 @@ function updateRetryData(policy: SystemErrorRetryPolicy, retryData?: RetryData,
// 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));
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);
retryData.retryInterval = Math.min(
policy.minRetryInterval + incrementDelta,
policy.maxRetryInterval
);
return retryData;
}
function retry(policy: SystemErrorRetryPolicy, request: WebResourceLike, operationResponse: HttpOperationResponse, retryData?: RetryData, err?: RetryError): Promise<HttpOperationResponse> {
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 (
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
return utils.delay(retryData.retryInterval)
.then(() => policy._nextPolicy.sendRequest(request.clone()))
.then(res => retry(policy, request, res, retryData, err))
.catch(err => retry(policy, request, operationResponse, retryData, err));
} else {
if (err != undefined) {
// If the operation failed in the end, return all errors instead of just the last one
err = retryData.error;
return Promise.reject(err);
try {
await utils.delay(retryData.retryInterval);
return policy._nextPolicy.sendRequest(request.clone());
} catch (error) {
return retry(policy, request, operationResponse, error, retryData);
}
return Promise.resolve(operationResponse);
} 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;
}
}
}

Просмотреть файл

@ -1,20 +1,28 @@
// 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 {
BaseRequestPolicy,
RequestPolicy,
RequestPolicyOptionsLike,
RequestPolicyFactory,
} from "./requestPolicy";
import { WebResourceLike } from "../webResource";
import { HttpOperationResponse } from "../httpOperationResponse";
import { Constants } from "../util/constants";
import { delay } from "../util/utils";
type ResponseHandler = (httpRequest: WebResourceLike, response: HttpOperationResponse) => Promise<HttpOperationResponse>;
type ResponseHandler = (
httpRequest: WebResourceLike,
response: HttpOperationResponse
) => Promise<HttpOperationResponse>;
const StatusCodes = Constants.HttpConstants.StatusCodes;
export function throttlingRetryPolicy(): RequestPolicyFactory {
return {
create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
return new ThrottlingRetryPolicy(nextPolicy, options);
}
},
};
}
@ -27,13 +35,17 @@ export function throttlingRetryPolicy(): RequestPolicyFactory {
export class ThrottlingRetryPolicy extends BaseRequestPolicy {
private _handleResponse: ResponseHandler;
constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike, _handleResponse?: ResponseHandler) {
constructor(
nextPolicy: RequestPolicy,
options: RequestPolicyOptionsLike,
_handleResponse?: ResponseHandler
) {
super(nextPolicy, options);
this._handleResponse = _handleResponse || this._defaultResponseHandler;
}
public async sendRequest(httpRequest: WebResourceLike): Promise<HttpOperationResponse> {
return this._nextPolicy.sendRequest(httpRequest.clone()).then(response => {
return this._nextPolicy.sendRequest(httpRequest.clone()).then((response) => {
if (response.status !== StatusCodes.TooManyRequests) {
return response;
} else {
@ -42,11 +54,18 @@ export class ThrottlingRetryPolicy extends BaseRequestPolicy {
});
}
private async _defaultResponseHandler(httpRequest: WebResourceLike, httpResponse: HttpOperationResponse): Promise<HttpOperationResponse> {
const retryAfterHeader: string | undefined = httpResponse.headers.get(Constants.HeaderConstants.RETRY_AFTER);
private async _defaultResponseHandler(
httpRequest: WebResourceLike,
httpResponse: HttpOperationResponse
): Promise<HttpOperationResponse> {
const retryAfterHeader: string | undefined = httpResponse.headers.get(
Constants.HeaderConstants.RETRY_AFTER
);
if (retryAfterHeader) {
const delayInMs: number | undefined = ThrottlingRetryPolicy.parseRetryAfterHeader(retryAfterHeader);
const delayInMs: number | undefined = ThrottlingRetryPolicy.parseRetryAfterHeader(
retryAfterHeader
);
if (delayInMs) {
return delay(delayInMs).then((_: any) => this._nextPolicy.sendRequest(httpRequest));
}

Просмотреть файл

@ -6,24 +6,35 @@ 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";
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
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);
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;
@ -36,18 +47,27 @@ export function getDefaultUserAgentValue(): string {
}
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;
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) {
constructor(
readonly _nextPolicy: RequestPolicy,
readonly _options: RequestPolicyOptionsLike,
protected headerKey: string,
protected headerValue: string
) {
super(_nextPolicy, _options);
}

Просмотреть файл

@ -10,16 +10,20 @@ 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 {
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()) || {}
}
headers: (headers && headers.rawHeaders()) || {},
},
};
if ((proxySettings.username && proxySettings.password)) {
if (proxySettings.username && proxySettings.password) {
tunnelOptions.proxy!.proxyAuth = `${proxySettings.username}:${proxySettings.password}`;
}
@ -30,13 +34,17 @@ export function createProxyAgent(requestUrl: string, proxySettings: ProxySetting
const proxyAgent = {
isHttps: isRequestHttps,
agent: createTunnel(isRequestHttps, isProxyHttps, tunnelOptions)
agent: createTunnel(isRequestHttps, isProxyHttps, tunnelOptions),
};
return proxyAgent;
}
export function createTunnel(isRequestHttps: boolean, isProxyHttps: boolean, tunnelOptions: tunnel.HttpsOverHttpsOptions): http.Agent | https.Agent {
export function createTunnel(
isRequestHttps: boolean,
isProxyHttps: boolean,
tunnelOptions: tunnel.HttpsOverHttpsOptions
): http.Agent | https.Agent {
if (isRequestHttps && isProxyHttps) {
return tunnel.httpsOverHttps(tunnelOptions);
} else if (isRequestHttps && !isProxyHttps) {

Просмотреть файл

@ -14,7 +14,14 @@ export class RestError extends Error {
request?: WebResourceLike;
response?: HttpOperationResponse;
body?: any;
constructor(message: string, 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;
@ -24,4 +31,4 @@ export class RestError extends Error {
Object.setPrototypeOf(this, RestError.prototype);
}
}
}

Просмотреть файл

@ -5,13 +5,18 @@ import * as base64 from "./util/base64";
import * as utils from "./util/utils";
export class Serializer {
constructor(public readonly modelMappers: { [key: string]: any } = {}, public readonly isXML?: boolean) { }
constructor(
public readonly modelMappers: { [key: string]: any } = {},
public readonly isXML?: boolean
) {}
validateConstraints(mapper: Mapper, value: any, objectName: string): void {
const failValidation = (constraintName: keyof MapperConstraints, constraintValue: any) => {
throw new Error(`"${objectName}" with value "${value}" should satisfy the constraint "${constraintName}": ${constraintValue}.`);
throw new Error(
`"${objectName}" with value "${value}" should satisfy the constraint "${constraintName}": ${constraintValue}.`
);
};
if (mapper.constraints && (value != undefined)) {
if (mapper.constraints && value != undefined) {
const {
ExclusiveMaximum,
ExclusiveMinimum,
@ -23,7 +28,7 @@ export class Serializer {
MinLength,
MultipleOf,
Pattern,
UniqueItems
UniqueItems,
} = mapper.constraints;
if (ExclusiveMaximum != undefined && value >= ExclusiveMaximum) {
failValidation("ExclusiveMaximum", ExclusiveMaximum);
@ -54,11 +59,14 @@ export class Serializer {
}
if (Pattern) {
const pattern: RegExp = typeof Pattern === "string" ? new RegExp(Pattern) : Pattern;
if ((typeof value !== "string") || (value.match(pattern) === null)) {
if (typeof value !== "string" || value.match(pattern) === null) {
failValidation("Pattern", Pattern);
}
}
if (UniqueItems && value.some((item: any, i: number, ar: Array<any>) => ar.indexOf(item) !== i)) {
if (
UniqueItems &&
value.some((item: any, i: number, ar: Array<any>) => ar.indexOf(item) !== i)
) {
failValidation("UniqueItems", UniqueItems);
}
}
@ -81,7 +89,7 @@ export class Serializer {
if (!objectName) {
objectName = mapper.serializedName!;
}
if (mapperType.match(/^Sequence$/ig) !== null) {
if (mapperType.match(/^Sequence$/gi) !== null) {
payload = [];
}
@ -116,24 +124,26 @@ export class Serializer {
} else {
// Validate Constraints if any
this.validateConstraints(mapper, object, objectName);
if (mapperType.match(/^any$/ig) !== null) {
if (mapperType.match(/^any$/gi) !== null) {
payload = object;
} else if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/ig) !== null) {
} else if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/gi) !== null) {
payload = serializeBasicTypes(mapperType, objectName, object);
} else if (mapperType.match(/^Enum$/ig) !== null) {
} else if (mapperType.match(/^Enum$/gi) !== null) {
const enumMapper: EnumMapper = mapper as EnumMapper;
payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object);
} else if (mapperType.match(/^(Date|DateTime|TimeSpan|DateTimeRfc1123|UnixTime)$/ig) !== null) {
} else if (
mapperType.match(/^(Date|DateTime|TimeSpan|DateTimeRfc1123|UnixTime)$/gi) !== null
) {
payload = serializeDateTypes(mapperType, object, objectName);
} else if (mapperType.match(/^ByteArray$/ig) !== null) {
} else if (mapperType.match(/^ByteArray$/gi) !== null) {
payload = serializeByteArrayType(objectName, object);
} else if (mapperType.match(/^Base64Url$/ig) !== null) {
} else if (mapperType.match(/^Base64Url$/gi) !== null) {
payload = serializeBase64UrlType(objectName, object);
} else if (mapperType.match(/^Sequence$/ig) !== null) {
} else if (mapperType.match(/^Sequence$/gi) !== null) {
payload = serializeSequenceType(this, mapper as SequenceMapper, object, objectName);
} else if (mapperType.match(/^Dictionary$/ig) !== null) {
} else if (mapperType.match(/^Dictionary$/gi) !== null) {
payload = serializeDictionaryType(this, mapper as DictionaryMapper, object, objectName);
} else if (mapperType.match(/^Composite$/ig) !== null) {
} else if (mapperType.match(/^Composite$/gi) !== null) {
payload = serializeCompositeType(this, mapper as CompositeMapper, object, objectName);
}
}
@ -172,7 +182,7 @@ export class Serializer {
objectName = mapper.serializedName!;
}
if (mapperType.match(/^Composite$/ig) !== null) {
if (mapperType.match(/^Composite$/gi) !== null) {
payload = deserializeCompositeType(this, mapper as CompositeMapper, responseBody, objectName);
} else {
if (this.isXML) {
@ -186,12 +196,12 @@ export class Serializer {
}
}
if (mapperType.match(/^Number$/ig) !== null) {
if (mapperType.match(/^Number$/gi) !== null) {
payload = parseFloat(responseBody);
if (isNaN(payload)) {
payload = responseBody;
}
} else if (mapperType.match(/^Boolean$/ig) !== null) {
} else if (mapperType.match(/^Boolean$/gi) !== null) {
if (responseBody === "true") {
payload = true;
} else if (responseBody === "false") {
@ -199,20 +209,25 @@ export class Serializer {
} else {
payload = responseBody;
}
} else if (mapperType.match(/^(String|Enum|Object|Stream|Uuid|TimeSpan|any)$/ig) !== null) {
} else if (mapperType.match(/^(String|Enum|Object|Stream|Uuid|TimeSpan|any)$/gi) !== null) {
payload = responseBody;
} else if (mapperType.match(/^(Date|DateTime|DateTimeRfc1123)$/ig) !== null) {
} else if (mapperType.match(/^(Date|DateTime|DateTimeRfc1123)$/gi) !== null) {
payload = new Date(responseBody);
} else if (mapperType.match(/^UnixTime$/ig) !== null) {
} else if (mapperType.match(/^UnixTime$/gi) !== null) {
payload = unixTimeToDate(responseBody);
} else if (mapperType.match(/^ByteArray$/ig) !== null) {
} else if (mapperType.match(/^ByteArray$/gi) !== null) {
payload = base64.decodeString(responseBody);
} else if (mapperType.match(/^Base64Url$/ig) !== null) {
} else if (mapperType.match(/^Base64Url$/gi) !== null) {
payload = base64UrlToByteArray(responseBody);
} else if (mapperType.match(/^Sequence$/ig) !== null) {
} else if (mapperType.match(/^Sequence$/gi) !== null) {
payload = deserializeSequenceType(this, mapper as SequenceMapper, responseBody, objectName);
} else if (mapperType.match(/^Dictionary$/ig) !== null) {
payload = deserializeDictionaryType(this, mapper as DictionaryMapper, responseBody, objectName);
} else if (mapperType.match(/^Dictionary$/gi) !== null) {
payload = deserializeDictionaryType(
this,
mapper as DictionaryMapper,
responseBody,
objectName
);
}
}
@ -226,7 +241,7 @@ export class Serializer {
function trimEnd(str: string, ch: string) {
let len = str.length;
while ((len - 1) >= 0 && str[len - 1] === ch) {
while (len - 1 >= 0 && str[len - 1] === ch) {
--len;
}
return str.substr(0, len);
@ -298,30 +313,36 @@ function unixTimeToDate(n: number): Date | undefined {
function serializeBasicTypes(typeName: string, objectName: string, value: any): any {
if (value !== null && value !== undefined) {
if (typeName.match(/^Number$/ig) !== null) {
if (typeName.match(/^Number$/gi) !== null) {
if (typeof value !== "number") {
throw new Error(`${objectName} with value ${value} must be of type number.`);
}
} else if (typeName.match(/^String$/ig) !== null) {
} else if (typeName.match(/^String$/gi) !== null) {
if (typeof value.valueOf() !== "string") {
throw new Error(`${objectName} with value "${value}" must be of type string.`);
}
} else if (typeName.match(/^Uuid$/ig) !== null) {
} else if (typeName.match(/^Uuid$/gi) !== null) {
if (!(typeof value.valueOf() === "string" && utils.isValidUuid(value))) {
throw new Error(`${objectName} with value "${value}" must be of type string and a valid uuid.`);
throw new Error(
`${objectName} with value "${value}" must be of type string and a valid uuid.`
);
}
} else if (typeName.match(/^Boolean$/ig) !== null) {
} else if (typeName.match(/^Boolean$/gi) !== null) {
if (typeof value !== "boolean") {
throw new Error(`${objectName} with value ${value} must be of type boolean.`);
}
} else if (typeName.match(/^Stream$/ig) !== null) {
} else if (typeName.match(/^Stream$/gi) !== null) {
const objectType = typeof value;
if (objectType !== "string" &&
if (
objectType !== "string" &&
objectType !== "function" &&
!(value instanceof ArrayBuffer) &&
!ArrayBuffer.isView(value) &&
!(typeof Blob === "function" && value instanceof Blob)) {
throw new Error(`${objectName} must be a string, Blob, ArrayBuffer, ArrayBufferView, or a function returning NodeJS.ReadableStream.`);
!(typeof Blob === "function" && value instanceof Blob)
) {
throw new Error(
`${objectName} must be a string, Blob, ArrayBuffer, ArrayBufferView, or a function returning NodeJS.ReadableStream.`
);
}
}
}
@ -330,7 +351,9 @@ function serializeBasicTypes(typeName: string, objectName: string, value: any):
function serializeEnumType(objectName: string, allowedValues: Array<any>, value: any): any {
if (!allowedValues) {
throw new Error(`Please provide a set of allowedValues to validate ${objectName} as an Enum Type.`);
throw new Error(
`Please provide a set of allowedValues to validate ${objectName} as an Enum Type.`
);
}
const isPresent = allowedValues.some((item) => {
if (typeof item.valueOf() === "string") {
@ -339,7 +362,11 @@ function serializeEnumType(objectName: string, allowedValues: Array<any>, value:
return item === value;
});
if (!isPresent) {
throw new Error(`${value} is not a valid value for ${objectName}. The valid values are: ${JSON.stringify(allowedValues)}.`);
throw new Error(
`${value} is not a valid value for ${objectName}. The valid values are: ${JSON.stringify(
allowedValues
)}.`
);
}
return value;
}
@ -366,34 +393,57 @@ function serializeBase64UrlType(objectName: string, value: any): any {
function serializeDateTypes(typeName: string, value: any, objectName: string) {
if (value != undefined) {
if (typeName.match(/^Date$/ig) !== null) {
if (!(value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value))))) {
if (typeName.match(/^Date$/gi) !== null) {
if (
!(
value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
)
) {
throw new Error(`${objectName} must be an instanceof Date or a string in ISO8601 format.`);
}
value = (value instanceof Date) ? value.toISOString().substring(0, 10) : new Date(value).toISOString().substring(0, 10);
} else if (typeName.match(/^DateTime$/ig) !== null) {
if (!(value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value))))) {
value =
value instanceof Date
? value.toISOString().substring(0, 10)
: new Date(value).toISOString().substring(0, 10);
} else if (typeName.match(/^DateTime$/gi) !== null) {
if (
!(
value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
)
) {
throw new Error(`${objectName} must be an instanceof Date or a string in ISO8601 format.`);
}
value = (value instanceof Date) ? value.toISOString() : new Date(value).toISOString();
} else if (typeName.match(/^DateTimeRfc1123$/ig) !== null) {
if (!(value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value))))) {
value = value instanceof Date ? value.toISOString() : new Date(value).toISOString();
} else if (typeName.match(/^DateTimeRfc1123$/gi) !== null) {
if (
!(
value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
)
) {
throw new Error(`${objectName} must be an instanceof Date or a string in RFC-1123 format.`);
}
value = (value instanceof Date) ? value.toUTCString() : new Date(value).toUTCString();
} else if (typeName.match(/^UnixTime$/ig) !== null) {
if (!(value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value))))) {
throw new Error(`${objectName} must be an instanceof Date or a string in RFC-1123/ISO8601 format ` +
`for it to be serialized in UnixTime/Epoch format.`);
value = value instanceof Date ? value.toUTCString() : new Date(value).toUTCString();
} else if (typeName.match(/^UnixTime$/gi) !== null) {
if (
!(
value instanceof Date ||
(typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
)
) {
throw new Error(
`${objectName} must be an instanceof Date or a string in RFC-1123/ISO8601 format ` +
`for it to be serialized in UnixTime/Epoch format.`
);
}
value = dateToUnixTime(value);
} else if (typeName.match(/^TimeSpan$/ig) !== null) {
} else if (typeName.match(/^TimeSpan$/gi) !== null) {
if (!utils.isDuration(value)) {
throw new Error(`${objectName} must be a string in ISO 8601 format. Instead was "${value}".`);
throw new Error(
`${objectName} must be a string in ISO 8601 format. Instead was "${value}".`
);
}
value = value;
}
@ -401,14 +451,21 @@ function serializeDateTypes(typeName: string, value: any, objectName: string) {
return value;
}
function serializeSequenceType(serializer: Serializer, mapper: SequenceMapper, object: any, objectName: string) {
function serializeSequenceType(
serializer: Serializer,
mapper: SequenceMapper,
object: any,
objectName: string
) {
if (!Array.isArray(object)) {
throw new Error(`${objectName} must be of type Array.`);
}
const elementType = mapper.type.element;
if (!elementType || typeof elementType !== "object") {
throw new Error(`element" metadata for an Array must be defined in the ` +
`mapper and it must of type "object" in ${objectName}.`);
throw new Error(
`element" metadata for an Array must be defined in the ` +
`mapper and it must of type "object" in ${objectName}.`
);
}
const tempArray = [];
for (let i = 0; i < object.length; i++) {
@ -417,14 +474,21 @@ function serializeSequenceType(serializer: Serializer, mapper: SequenceMapper, o
return tempArray;
}
function serializeDictionaryType(serializer: Serializer, mapper: DictionaryMapper, object: any, objectName: string) {
function serializeDictionaryType(
serializer: Serializer,
mapper: DictionaryMapper,
object: any,
objectName: string
) {
if (typeof object !== "object") {
throw new Error(`${objectName} must be of type object.`);
}
const valueType = mapper.type.value;
if (!valueType || typeof valueType !== "object") {
throw new Error(`"value" metadata for a Dictionary must be defined in the ` +
`mapper and it must of type "object" in ${objectName}.`);
throw new Error(
`"value" metadata for a Dictionary must be defined in the ` +
`mapper and it must of type "object" in ${objectName}.`
);
}
const tempDictionary: { [key: string]: any } = {};
for (const key of Object.keys(object)) {
@ -438,12 +502,22 @@ function serializeDictionaryType(serializer: Serializer, mapper: DictionaryMappe
* @param serializer the serializer containing the entire set of mappers
* @param mapper the composite mapper to resolve
*/
function resolveModelProperties(serializer: Serializer, mapper: CompositeMapper, objectName: string): { [propertyName: string]: Mapper } {
function resolveModelProperties(
serializer: Serializer,
mapper: CompositeMapper,
objectName: string
): { [propertyName: string]: Mapper } {
let modelProps = mapper.type.modelProperties;
if (!modelProps) {
const className = mapper.type.className;
if (!className) {
throw new Error(`Class name for model "${objectName}" is not provided in the mapper "${JSON.stringify(mapper, undefined, 2)}".`);
throw new Error(
`Class name for model "${objectName}" is not provided in the mapper "${JSON.stringify(
mapper,
undefined,
2
)}".`
);
}
const modelMapper = serializer.modelMappers[className];
@ -452,15 +526,24 @@ function resolveModelProperties(serializer: Serializer, mapper: CompositeMapper,
}
modelProps = modelMapper.type.modelProperties;
if (!modelProps) {
throw new Error(`modelProperties cannot be null or undefined in the ` +
`mapper "${JSON.stringify(modelMapper)}" of type "${className}" for object "${objectName}".`);
throw new Error(
`modelProperties cannot be null or undefined in the ` +
`mapper "${JSON.stringify(
modelMapper
)}" of type "${className}" for object "${objectName}".`
);
}
}
return modelProps;
}
function serializeCompositeType(serializer: Serializer, mapper: CompositeMapper, object: any, objectName: string) {
function serializeCompositeType(
serializer: Serializer,
mapper: CompositeMapper,
object: any,
objectName: string
) {
if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) {
mapper = getPolymorphicMapper(serializer, mapper, object, "clientName");
}
@ -488,7 +571,7 @@ function serializeCompositeType(serializer: Serializer, mapper: CompositeMapper,
for (const pathName of paths) {
const childObject = parentObject[pathName];
if ((childObject == undefined) && (object[key] != undefined)) {
if (childObject == undefined && object[key] != undefined) {
parentObject[pathName] = {};
}
parentObject = parentObject[pathName];
@ -496,17 +579,26 @@ function serializeCompositeType(serializer: Serializer, mapper: CompositeMapper,
}
if (parentObject != undefined) {
const propertyObjectName = propertyMapper.serializedName !== ""
? objectName + "." + propertyMapper.serializedName
: objectName;
const propertyObjectName =
propertyMapper.serializedName !== ""
? objectName + "." + propertyMapper.serializedName
: objectName;
let toSerialize = object[key];
const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper);
if (polymorphicDiscriminator && polymorphicDiscriminator.clientName === key && toSerialize == undefined) {
if (
polymorphicDiscriminator &&
polymorphicDiscriminator.clientName === key &&
toSerialize == undefined
) {
toSerialize = mapper.serializedName;
}
const serializedValue = serializer.serialize(propertyMapper, toSerialize, propertyObjectName);
const serializedValue = serializer.serialize(
propertyMapper,
toSerialize,
propertyObjectName
);
if (serializedValue !== undefined && propName != undefined) {
if (propertyMapper.xmlIsAttribute) {
// $ is the key attributes are kept under in xml2js.
@ -527,9 +619,13 @@ function serializeCompositeType(serializer: Serializer, mapper: CompositeMapper,
if (additionalPropertiesMapper) {
const propNames = Object.keys(modelProps);
for (const clientPropName in object) {
const isAdditionalProperty = propNames.every(pn => pn !== clientPropName);
const isAdditionalProperty = propNames.every((pn) => pn !== clientPropName);
if (isAdditionalProperty) {
payload[clientPropName] = serializer.serialize(additionalPropertiesMapper, object[clientPropName], objectName + '["' + clientPropName + '"]');
payload[clientPropName] = serializer.serialize(
additionalPropertiesMapper,
object[clientPropName],
objectName + '["' + clientPropName + '"]'
);
}
}
}
@ -543,7 +639,12 @@ function isSpecialXmlProperty(propertyName: string): boolean {
return ["$", "_"].includes(propertyName);
}
function deserializeCompositeType(serializer: Serializer, mapper: CompositeMapper, responseBody: any, objectName: string): any {
function deserializeCompositeType(
serializer: Serializer,
mapper: CompositeMapper,
responseBody: any,
objectName: string
): any {
if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) {
mapper = getPolymorphicMapper(serializer, mapper, responseBody, "serializedName");
}
@ -567,7 +668,11 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
const dictionary: any = {};
for (const headerKey of Object.keys(responseBody)) {
if (headerKey.startsWith(headerCollectionPrefix)) {
dictionary[headerKey.substring(headerCollectionPrefix.length)] = serializer.deserialize((propertyMapper as DictionaryMapper).type.value, responseBody[headerKey], propertyObjectName);
dictionary[headerKey.substring(headerCollectionPrefix.length)] = serializer.deserialize(
(propertyMapper as DictionaryMapper).type.value,
responseBody[headerKey],
propertyObjectName
);
}
handledPropertyNames.push(headerKey);
@ -575,7 +680,11 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
instance[key] = dictionary;
} else if (serializer.isXML) {
if (propertyMapper.xmlIsAttribute && responseBody.$) {
instance[key] = serializer.deserialize(propertyMapper, responseBody.$[xmlName!], propertyObjectName);
instance[key] = serializer.deserialize(
propertyMapper,
responseBody.$[xmlName!],
propertyObjectName
);
} else {
const propertyName = xmlElementName || xmlName || serializedName;
let unwrappedProperty = responseBody[propertyName!];
@ -588,7 +697,11 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
unwrappedProperty = [];
}
}
instance[key] = serializer.deserialize(propertyMapper, unwrappedProperty, propertyObjectName);
instance[key] = serializer.deserialize(
propertyMapper,
unwrappedProperty,
propertyObjectName
);
}
} else {
// deserialize the property if it is present in the provided responseBody instance
@ -610,7 +723,11 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
// the clientName transformation of the polymorphicDiscriminator (ex: "fishtype") and
// the transformation of model property name (ex: "fishtype") is done consistently.
// Hence, it is a safer bet to rely on the clientName of the polymorphicDiscriminator.
if (polymorphicDiscriminator && key === polymorphicDiscriminator.clientName && propertyInstance == undefined) {
if (
polymorphicDiscriminator &&
key === polymorphicDiscriminator.clientName &&
propertyInstance == undefined
) {
propertyInstance = mapper.serializedName;
}
@ -620,7 +737,11 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
propertyInstance = responseBody[key];
instance = serializer.deserialize(propertyMapper, propertyInstance, propertyObjectName);
} else if (propertyInstance !== undefined || propertyMapper.defaultValue !== undefined) {
serializedValue = serializer.deserialize(propertyMapper, propertyInstance, propertyObjectName);
serializedValue = serializer.deserialize(
propertyMapper,
propertyInstance,
propertyObjectName
);
instance[key] = serializedValue;
}
}
@ -640,12 +761,20 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
for (const responsePropName in responseBody) {
if (isAdditionalProperty(responsePropName)) {
instance[responsePropName] = serializer.deserialize(additionalPropertiesMapper, responseBody[responsePropName], objectName + '["' + responsePropName + '"]');
instance[responsePropName] = serializer.deserialize(
additionalPropertiesMapper,
responseBody[responsePropName],
objectName + '["' + responsePropName + '"]'
);
}
}
} else if (responseBody) {
for (const key of Object.keys(responseBody)) {
if (instance[key] === undefined && !handledPropertyNames.includes(key) && !isSpecialXmlProperty(key)) {
if (
instance[key] === undefined &&
!handledPropertyNames.includes(key) &&
!isSpecialXmlProperty(key)
) {
instance[key] = responseBody[key];
}
}
@ -654,12 +783,19 @@ function deserializeCompositeType(serializer: Serializer, mapper: CompositeMappe
return instance;
}
function deserializeDictionaryType(serializer: Serializer, mapper: DictionaryMapper, responseBody: any, objectName: string): any {
function deserializeDictionaryType(
serializer: Serializer,
mapper: DictionaryMapper,
responseBody: any,
objectName: string
): any {
/*jshint validthis: true */
const value = mapper.type.value;
if (!value || typeof value !== "object") {
throw new Error(`"value" metadata for a Dictionary must be defined in the ` +
`mapper and it must of type "object" in ${objectName}`);
throw new Error(
`"value" metadata for a Dictionary must be defined in the ` +
`mapper and it must of type "object" in ${objectName}`
);
}
if (responseBody) {
const tempDictionary: { [key: string]: any } = {};
@ -671,12 +807,19 @@ function deserializeDictionaryType(serializer: Serializer, mapper: DictionaryMap
return responseBody;
}
function deserializeSequenceType(serializer: Serializer, mapper: SequenceMapper, responseBody: any, objectName: string): any {
function deserializeSequenceType(
serializer: Serializer,
mapper: SequenceMapper,
responseBody: any,
objectName: string
): any {
/*jshint validthis: true */
const element = mapper.type.element;
if (!element || typeof element !== "object") {
throw new Error(`element" metadata for an Array must be defined in the ` +
`mapper and it must of type "object" in ${objectName}`);
throw new Error(
`element" metadata for an Array must be defined in the ` +
`mapper and it must of type "object" in ${objectName}`
);
}
if (responseBody) {
if (!Array.isArray(responseBody)) {
@ -693,7 +836,12 @@ function deserializeSequenceType(serializer: Serializer, mapper: SequenceMapper,
return responseBody;
}
function getPolymorphicMapper(serializer: Serializer, mapper: CompositeMapper, object: any, polymorphicPropertyName: "clientName" | "serializedName"): CompositeMapper {
function getPolymorphicMapper(
serializer: Serializer,
mapper: CompositeMapper,
object: any,
polymorphicPropertyName: "clientName" | "serializedName"
): CompositeMapper {
const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper);
if (polymorphicDiscriminator) {
const discriminatorName = polymorphicDiscriminator[polymorphicPropertyName];
@ -701,9 +849,10 @@ function getPolymorphicMapper(serializer: Serializer, mapper: CompositeMapper, o
const discriminatorValue = object[discriminatorName];
if (discriminatorValue != undefined) {
const typeName = mapper.type.uberParent || mapper.type.className;
const indexDiscriminator = discriminatorValue === typeName
? discriminatorValue
: typeName + "." + discriminatorValue;
const indexDiscriminator =
discriminatorValue === typeName
? discriminatorValue
: typeName + "." + discriminatorValue;
const polymorphicMapper = serializer.modelMappers.discriminators[indexDiscriminator];
if (polymorphicMapper) {
mapper = polymorphicMapper;
@ -714,14 +863,23 @@ function getPolymorphicMapper(serializer: Serializer, mapper: CompositeMapper, o
return mapper;
}
function getPolymorphicDiscriminatorRecursively(serializer: Serializer, mapper: CompositeMapper): PolymorphicDiscriminator | undefined {
return mapper.type.polymorphicDiscriminator
|| getPolymorphicDiscriminatorSafely(serializer, mapper.type.uberParent)
|| getPolymorphicDiscriminatorSafely(serializer, mapper.type.className);
function getPolymorphicDiscriminatorRecursively(
serializer: Serializer,
mapper: CompositeMapper
): PolymorphicDiscriminator | undefined {
return (
mapper.type.polymorphicDiscriminator ||
getPolymorphicDiscriminatorSafely(serializer, mapper.type.uberParent) ||
getPolymorphicDiscriminatorSafely(serializer, mapper.type.className)
);
}
function getPolymorphicDiscriminatorSafely(serializer: Serializer, typeName?: string) {
return (typeName && serializer.modelMappers[typeName] && serializer.modelMappers[typeName].type.polymorphicDiscriminator);
return (
typeName &&
serializer.modelMappers[typeName] &&
serializer.modelMappers[typeName].type.polymorphicDiscriminator
);
}
export interface MapperConstraints {
@ -738,23 +896,29 @@ export interface MapperConstraints {
MultipleOf?: number;
}
export type MapperType = SimpleMapperType | CompositeMapperType | SequenceMapperType | DictionaryMapperType | EnumMapperType;
export type MapperType =
| SimpleMapperType
| CompositeMapperType
| SequenceMapperType
| DictionaryMapperType
| EnumMapperType;
export interface SimpleMapperType {
name: "Base64Url"
| "Boolean"
| "ByteArray"
| "Date"
| "DateTime"
| "DateTimeRfc1123"
| "Object"
| "Stream"
| "String"
| "TimeSpan"
| "UnixTime"
| "Uuid"
| "Number"
| "any";
name:
| "Base64Url"
| "Boolean"
| "ByteArray"
| "Date"
| "DateTime"
| "DateTimeRfc1123"
| "Object"
| "Stream"
| "String"
| "TimeSpan"
| "UnixTime"
| "Uuid"
| "Number"
| "any";
}
export interface CompositeMapperType {
@ -838,11 +1002,9 @@ export function serializeObject(toSerialize: any): any {
if (toSerialize instanceof Uint8Array) {
toSerialize = base64.encodeByteArray(toSerialize);
return toSerialize;
}
else if (toSerialize instanceof Date) {
} else if (toSerialize instanceof Date) {
return toSerialize.toISOString();
}
else if (Array.isArray(toSerialize)) {
} else if (Array.isArray(toSerialize)) {
const array = [];
for (let i = 0; i < toSerialize.length; i++) {
array.push(serializeObject(toSerialize[i]));
@ -885,5 +1047,5 @@ export const MapperType = strEnum([
"String",
"Stream",
"TimeSpan",
"UnixTime"
"UnixTime",
]);

Просмотреть файл

@ -8,14 +8,31 @@ 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 {
getPathStringFromParameter,
getPathStringFromParameterPath,
OperationParameter,
ParameterPath,
} from "./operationParameter";
import { isStreamOperation, OperationSpec } from "./operationSpec";
import { deserializationPolicy, DeserializationContentTypes } from "./policies/deserializationPolicy";
import {
deserializationPolicy,
DeserializationContentTypes,
} from "./policies/deserializationPolicy";
import { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
import { generateClientRequestIdPolicy } from "./policies/generateClientRequestIdPolicy";
import { userAgentPolicy, getDefaultUserAgentHeaderName, getDefaultUserAgentValue } from "./policies/userAgentPolicy";
import {
userAgentPolicy,
getDefaultUserAgentHeaderName,
getDefaultUserAgentValue,
} from "./policies/userAgentPolicy";
import { redirectPolicy } from "./policies/redirectPolicy";
import { RequestPolicy, RequestPolicyFactory, RequestPolicyOptions, RequestPolicyOptionsLike } from "./policies/requestPolicy";
import {
RequestPolicy,
RequestPolicyFactory,
RequestPolicyOptions,
RequestPolicyOptionsLike,
} from "./policies/requestPolicy";
import { rpRegistrationPolicy } from "./policies/rpRegistrationPolicy";
import { signingPolicy } from "./policies/signingPolicy";
import { systemErrorRetryPolicy } from "./policies/systemErrorRetryPolicy";
@ -24,7 +41,13 @@ import { CompositeMapper, DictionaryMapper, Mapper, MapperType, Serializer } fro
import { URLBuilder } from "./url";
import * as utils from "./util/utils";
import { stringifyXML } from "./util/xml";
import { RequestOptionsBase, RequestPrepareOptions, WebResourceLike, isWebResourceLike, WebResource } from "./webResource";
import {
RequestOptionsBase,
RequestPrepareOptions,
WebResourceLike,
isWebResourceLike,
WebResource,
} from "./webResource";
import { OperationResponse } from "./operationResponse";
import { ServiceCallback } from "./util/utils";
import { agentPolicy } from "./policies/agentPolicy";
@ -33,7 +56,6 @@ import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy";
import { Agent } from "http";
import { AzureIdentityCredentialAdapter } from "./credentials/azureIdentityTokenCredentialAdapter";
/**
* HTTP proxy settings (Node.js only)
*/
@ -61,7 +83,9 @@ export interface ServiceClientOptions {
* 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[]));
requestPolicyFactories?:
| RequestPolicyFactory[]
| ((defaultRequestPolicyFactories: RequestPolicyFactory[]) => void | RequestPolicyFactory[]);
/**
* The HttpClient that will be used to send HTTP requests.
*/
@ -134,7 +158,6 @@ export class ServiceClient {
*/
protected requestContentType?: string;
/**
* The HTTP client that will be used to send requests.
*/
@ -150,7 +173,10 @@ export class ServiceClient {
* @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) {
constructor(
credentials?: ServiceClientCredentials | TokenCredential,
options?: ServiceClientOptions
) {
if (!options) {
options = {};
}
@ -162,7 +188,6 @@ export class ServiceClient {
serviceClientCredentials = credentials;
}
if (serviceClientCredentials && !serviceClientCredentials.signRequest) {
throw new Error("credentials argument needs to implement signRequest method");
}
@ -175,9 +200,14 @@ export class ServiceClient {
if (Array.isArray(options.requestPolicyFactories)) {
requestPolicyFactories = options.requestPolicyFactories;
} else {
requestPolicyFactories = createDefaultRequestPolicyFactories(serviceClientCredentials, options);
requestPolicyFactories = createDefaultRequestPolicyFactories(
serviceClientCredentials,
options
);
if (options.requestPolicyFactories) {
const newRequestPolicyFactories: void | RequestPolicyFactory[] = options.requestPolicyFactories(requestPolicyFactories);
const newRequestPolicyFactories:
| void
| RequestPolicyFactory[] = options.requestPolicyFactories(requestPolicyFactories);
if (newRequestPolicyFactories) {
requestPolicyFactories = newRequestPolicyFactories;
}
@ -210,7 +240,10 @@ export class ServiceClient {
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);
httpPipeline = this._requestPolicyFactories[i].create(
httpPipeline,
this._requestPolicyOptions
);
}
}
return httpPipeline.sendRequest(httpRequest);
@ -222,7 +255,11 @@ export class ServiceClient {
* @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> {
sendOperationRequest(
operationArguments: OperationArguments,
operationSpec: OperationSpec,
callback?: ServiceCallback<any>
): Promise<RestResponse> {
if (typeof operationArguments.options === "function") {
callback = operationArguments.options;
operationArguments.options = undefined;
@ -234,7 +271,9 @@ export class ServiceClient {
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.");
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;
@ -246,19 +285,40 @@ export class ServiceClient {
}
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));
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);
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);
let queryParameterValue: any = getOperationArgumentValueFromParameter(
this,
operationArguments,
queryParameter,
operationSpec.serializer
);
if (queryParameterValue != undefined) {
queryParameterValue = operationSpec.serializer.serialize(queryParameter.mapper, queryParameterValue, getPathStringFromParameter(queryParameter));
queryParameterValue = operationSpec.serializer.serialize(
queryParameter.mapper,
queryParameterValue,
getPathStringFromParameter(queryParameter)
);
if (queryParameter.collectionFormat != undefined) {
if (queryParameter.collectionFormat === QueryCollectionFormat.Multi) {
if (queryParameterValue.length === 0) {
@ -269,26 +329,39 @@ export class ServiceClient {
queryParameterValue[index] = item == undefined ? "" : item.toString();
}
}
} else if (queryParameter.collectionFormat === QueryCollectionFormat.Ssv || queryParameter.collectionFormat === QueryCollectionFormat.Tsv) {
} 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) {
if (
queryParameterValue[index] !== undefined &&
queryParameterValue[index] !== null
) {
queryParameterValue[index] = encodeURIComponent(queryParameterValue[index]);
}
}
}
else {
} else {
queryParameterValue = encodeURIComponent(queryParameterValue);
}
}
if (queryParameter.collectionFormat != undefined && queryParameter.collectionFormat !== QueryCollectionFormat.Multi && queryParameter.collectionFormat !== QueryCollectionFormat.Ssv && queryParameter.collectionFormat !== QueryCollectionFormat.Tsv) {
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);
requestUrl.setQueryParameter(
queryParameter.mapper.serializedName || getPathStringFromParameter(queryParameter),
queryParameterValue
);
}
}
}
@ -301,16 +374,30 @@ export class ServiceClient {
if (operationSpec.headerParameters) {
for (const headerParameter of operationSpec.headerParameters) {
let headerValue: any = getOperationArgumentValueFromParameter(this, operationArguments, headerParameter, operationSpec.serializer);
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;
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);
httpRequest.headers.set(
headerParameter.mapper.serializedName ||
getPathStringFromParameter(headerParameter),
headerValue
);
}
}
}
@ -349,8 +436,9 @@ export class ServiceClient {
httpRequest.streamResponseBody = isStreamOperation(operationSpec);
}
result = this.sendRequest(httpRequest)
.then(res => flattenResponse(res, operationSpec.responses[res.status]));
result = this.sendRequest(httpRequest).then((res) =>
flattenResponse(res, operationSpec.responses[res.status])
);
} catch (error) {
result = Promise.reject(error);
}
@ -359,47 +447,86 @@ export class ServiceClient {
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));
.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 {
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);
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 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 });
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, " ")}.`);
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);
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));
const formDataParameterPropertyName: string =
formDataParameter.mapper.serializedName || getPathStringFromParameter(formDataParameter);
httpRequest.formData[formDataParameterPropertyName] = operationSpec.serializer.serialize(
formDataParameter.mapper,
formDataParameterValue,
getPathStringFromParameter(formDataParameter)
);
}
}
}
@ -409,7 +536,10 @@ function isRequestPolicyFactory(instance: any): instance is RequestPolicyFactory
return typeof instance.create === "function";
}
function getValueOrFunctionResult(value: undefined | string | ((defaultValue: string) => string), defaultValueCreator: (() => string)): string {
function getValueOrFunctionResult(
value: undefined | string | ((defaultValue: string) => string),
defaultValueCreator: () => string
): string {
let result: string;
if (typeof value === "string") {
result = value;
@ -422,7 +552,10 @@ function getValueOrFunctionResult(value: undefined | string | ((defaultValue: st
return result;
}
function createDefaultRequestPolicyFactories(credentials: ServiceClientCredentials | RequestPolicyFactory | undefined, options: ServiceClientOptions): RequestPolicyFactory[] {
function createDefaultRequestPolicyFactories(
credentials: ServiceClientCredentials | RequestPolicyFactory | undefined,
options: ServiceClientOptions
): RequestPolicyFactory[] {
const factories: RequestPolicyFactory[] = [];
if (options.generateClientRequestIdHeader) {
@ -437,8 +570,14 @@ function createDefaultRequestPolicyFactories(credentials: ServiceClientCredentia
}
}
const userAgentHeaderName: string = getValueOrFunctionResult(options.userAgentHeaderName, getDefaultUserAgentHeaderName);
const userAgentHeaderValue: string = getValueOrFunctionResult(options.userAgent, getDefaultUserAgentValue);
const userAgentHeaderName: string = getValueOrFunctionResult(
options.userAgentHeaderName,
getDefaultUserAgentHeaderName
);
const userAgentHeaderValue: string = getValueOrFunctionResult(
options.userAgent,
getDefaultUserAgentValue
);
if (userAgentHeaderName && userAgentHeaderValue) {
factories.push(userAgentPolicy({ key: userAgentHeaderName, value: userAgentHeaderValue }));
}
@ -485,11 +624,28 @@ export function getPropertyParent(parent: PropertyParent, propertyPath: string[]
return parent;
}
function getOperationArgumentValueFromParameter(serviceClient: ServiceClient, operationArguments: OperationArguments, parameter: OperationParameter, serializer: Serializer): any {
return getOperationArgumentValueFromParameterPath(serviceClient, operationArguments, parameter.parameterPath, parameter.mapper, serializer);
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 {
export function getOperationArgumentValueFromParameterPath(
serviceClient: ServiceClient,
operationArguments: OperationArguments,
parameterPath: ParameterPath,
parameterMapper: Mapper,
serializer: Serializer
): any {
let value: any;
if (typeof parameterPath === "string") {
parameterPath = [parameterPath];
@ -499,20 +655,28 @@ export function getOperationArgumentValueFromParameterPath(serviceClient: Servic
if (parameterMapper.isConstant) {
value = parameterMapper.defaultValue;
} else {
let propertySearchResult: PropertySearchResult = getPropertyFromParameterPath(operationArguments, parameterPath);
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);
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);
const parameterPathString: string = getPathStringFromParameterPath(
parameterPath,
parameterMapper
);
serializer.serialize(parameterMapper, value, parameterPathString);
}
} else {
@ -521,11 +685,22 @@ export function getOperationArgumentValueFromParameterPath(serviceClient: Servic
}
for (const propertyName in parameterPath) {
const propertyMapper: Mapper = (parameterMapper as CompositeMapper).type.modelProperties![propertyName];
const propertyMapper: Mapper = (parameterMapper as CompositeMapper).type.modelProperties![
propertyName
];
const propertyPath: ParameterPath = parameterPath[propertyName];
const propertyValue: any = getOperationArgumentValueFromParameterPath(serviceClient, operationArguments, propertyPath, propertyMapper, serializer);
const propertyValue: any = getOperationArgumentValueFromParameterPath(
serviceClient,
operationArguments,
propertyPath,
propertyMapper,
serializer
);
// Serialize just for validation purposes.
const propertyPathString: string = getPathStringFromParameterPath(propertyPath, propertyMapper);
const propertyPathString: string = getPathStringFromParameterPath(
propertyPath,
propertyMapper
);
serializer.serialize(propertyMapper, propertyValue, propertyPathString);
if (propertyValue !== undefined) {
if (!value) {
@ -543,7 +718,10 @@ interface PropertySearchResult {
propertyFound: boolean;
}
function getPropertyFromParameterPath(parent: { [parameterName: string]: any }, parameterPath: string[]): PropertySearchResult {
function getPropertyFromParameterPath(
parent: { [parameterName: string]: any },
parameterPath: string[]
): PropertySearchResult {
const result: PropertySearchResult = { propertyFound: false };
let i = 0;
for (; i < parameterPath.length; ++i) {
@ -562,13 +740,16 @@ function getPropertyFromParameterPath(parent: { [parameterName: string]: any },
return result;
}
export function flattenResponse(_response: HttpOperationResponse, responseSpec: OperationResponse | undefined): RestResponse {
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
value: _response,
});
if (bodyMapper) {
@ -577,19 +758,19 @@ export function flattenResponse(_response: HttpOperationResponse, responseSpec:
return addOperationResponse({
...parsedHeaders,
blobBody: _response.blobBody,
readableStreamBody: _response.readableStreamBody
readableStreamBody: _response.readableStreamBody,
});
}
const modelProperties = typeName === "Composite" && (bodyMapper as CompositeMapper).type.modelProperties || {};
const isPageableResponse = Object.keys(modelProperties).some(k => modelProperties[k].serializedName === "");
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 parsedBody = Array.isArray(_response.parsedBody) ? _response.parsedBody : [];
const arrayResponse = [...parsedBody] as RestResponse & any[];
for (const key of Object.keys(modelProperties)) {
@ -610,21 +791,25 @@ export function flattenResponse(_response: HttpOperationResponse, responseSpec:
if (typeName === "Composite" || typeName === "Dictionary") {
return addOperationResponse({
...parsedHeaders,
..._response.parsedBody
..._response.parsedBody,
});
}
}
if (bodyMapper || _response.request.method === "HEAD" || utils.isPrimitiveType(_response.parsedBody)) {
if (
bodyMapper ||
_response.request.method === "HEAD" ||
utils.isPrimitiveType(_response.parsedBody)
) {
// primitive body types and HEAD booleans
return addOperationResponse({
...parsedHeaders,
body: _response.parsedBody
body: _response.parsedBody,
});
}
return addOperationResponse({
...parsedHeaders,
..._response.parsedBody
..._response.parsedBody,
});
}

Просмотреть файл

@ -83,40 +83,40 @@ export class URLQuery {
for (let i = 0; i < text.length; ++i) {
const currentCharacter: string = text[i];
switch (currentState) {
case "ParameterName":
switch (currentCharacter) {
case "=":
currentState = "ParameterValue";
case "ParameterName":
switch (currentCharacter) {
case "=":
currentState = "ParameterValue";
break;
case "&":
parameterName = "";
parameterValue = "";
break;
default:
parameterName += currentCharacter;
break;
}
break;
case "&":
parameterName = "";
parameterValue = "";
case "ParameterValue":
switch (currentCharacter) {
case "&":
result.set(parameterName, parameterValue);
parameterName = "";
parameterValue = "";
currentState = "ParameterName";
break;
default:
parameterValue += currentCharacter;
break;
}
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);
throw new Error("Unrecognized URLQuery parse state: " + currentState);
}
}
if (currentState === "ParameterValue") {
@ -290,31 +290,31 @@ export class URLBuilder {
const token: URLToken | undefined = tokenizer.current();
if (token) {
switch (token.type) {
case "SCHEME":
this._scheme = token.text || undefined;
break;
case "SCHEME":
this._scheme = token.text || undefined;
break;
case "HOST":
this._host = token.text || undefined;
break;
case "HOST":
this._host = token.text || undefined;
break;
case "PORT":
this._port = 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 "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;
case "QUERY":
this._query = URLQuery.parse(token.text);
break;
default:
throw new Error(`Unrecognized URLTokenType: ${token.type}`);
default:
throw new Error(`Unrecognized URLTokenType: ${token.type}`);
}
}
}
@ -375,8 +375,7 @@ type URLTokenizerState = "SCHEME" | "SCHEME_OR_HOST" | "HOST" | "PORT" | "PATH"
type URLTokenType = "SCHEME" | "HOST" | "PORT" | "PATH" | "QUERY";
export class URLToken {
public constructor(public readonly text: string, public readonly type: URLTokenType) {
}
public constructor(public readonly text: string, public readonly type: URLTokenType) {}
public static scheme(text: string): URLToken {
return new URLToken(text, "SCHEME");
@ -405,9 +404,11 @@ export class URLToken {
*/
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' */);
return (
(48 /* '0' */ <= characterCode && characterCode <= 57) /* '9' */ ||
(65 /* 'A' */ <= characterCode && characterCode <= 90) /* 'Z' */ ||
(97 /* 'a' */ <= characterCode && characterCode <= 122) /* 'z' */
);
}
/**
@ -441,39 +442,38 @@ export class URLTokenizer {
this._currentToken = undefined;
} else {
switch (this._currentState) {
case "SCHEME":
nextScheme(this);
break;
case "SCHEME":
nextScheme(this);
break;
case "SCHEME_OR_HOST":
nextSchemeOrHost(this);
break;
case "SCHEME_OR_HOST":
nextSchemeOrHost(this);
break;
case "HOST":
nextHost(this);
break;
case "HOST":
nextHost(this);
break;
case "PORT":
nextPort(this);
break;
case "PORT":
nextPort(this);
break;
case "PATH":
nextPath(this);
break;
case "PATH":
nextPath(this);
break;
case "QUERY":
nextQuery(this);
break;
case "QUERY":
nextQuery(this);
break;
default:
throw new Error(`Unrecognized URLTokenizerState: ${this._currentState}`);
default:
throw new Error(`Unrecognized URLTokenizerState: ${this._currentState}`);
}
}
return !!this._currentToken;
}
}
/**
* Read the remaining characters from this Tokenizer's character stream.
*/
@ -558,7 +558,10 @@ function readWhileLetterOrDigit(tokenizer: URLTokenizer): string {
* the end of the character stream is reached.
*/
function readUntilCharacter(tokenizer: URLTokenizer, ...terminatingCharacters: string[]): string {
return readWhile(tokenizer, (character: string) => terminatingCharacters.indexOf(character) === -1);
return readWhile(
tokenizer,
(character: string) => terminatingCharacters.indexOf(character) === -1
);
}
function nextScheme(tokenizer: URLTokenizer): void {

Просмотреть файл

@ -16,7 +16,7 @@ export function encodeString(value: string): string {
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);
const bufferValue = value instanceof Buffer ? value : Buffer.from(value.buffer as ArrayBuffer);
return bufferValue.toString("base64");
}

Просмотреть файл

@ -7,7 +7,7 @@ export const Constants = {
* @const
* @type {string}
*/
msRestVersion: "2.2.0",
msRestVersion: "2.2.1",
/**
* Specifies HTTP.
@ -55,12 +55,12 @@ export const Constants = {
POST: "POST",
MERGE: "MERGE",
HEAD: "HEAD",
PATCH: "PATCH"
PATCH: "PATCH",
},
StatusCodes: {
TooManyRequests: 429
}
TooManyRequests: 429,
},
},
/**
@ -93,6 +93,6 @@ export const Constants = {
* @const
* @type {string}
*/
USER_AGENT: "User-Agent"
}
USER_AGENT: "User-Agent",
},
};

Просмотреть файл

@ -10,7 +10,11 @@ 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;
export const isNode =
typeof process !== "undefined" &&
!!process.version &&
!!process.versions &&
!!process.versions.node;
/**
* Checks if a parsed URL is HTTPS
@ -77,7 +81,10 @@ export function stripRequest(request: WebResourceLike): WebResourceLike {
* @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");
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);
}
@ -89,7 +96,7 @@ export function isValidUuid(uuid: string): boolean {
*
* @return {any[]} An array of values of the given object.
*/
export function objectValues(obj: { [key: string]: any; }): any[] {
export function objectValues(obj: { [key: string]: any }): any[] {
const result: any[] = [];
if (obj && obj instanceof Object) {
for (const key in obj) {
@ -98,8 +105,13 @@ export function objectValues(obj: { [key: string]: any; }): any[] {
}
}
} 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.`);
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;
}
@ -140,7 +152,7 @@ export function executePromisesSequentially(promiseFactories: Array<any>, kickst
*
* @returns {object} Returns the merged target object.
*/
export function mergeObjects(source: { [key: string]: any; }, target: { [key: string]: any; }) {
export function mergeObjects(source: { [key: string]: any }, target: { [key: string]: any }) {
Object.keys(source).forEach((key) => {
target[key] = source[key];
});
@ -168,7 +180,12 @@ export interface ServiceCallback<TResult> {
* @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;
(
err: Error | RestError | null,
result?: TResult,
request?: WebResourceLike,
response?: HttpOperationResponse
): void;
}
/**
@ -182,11 +199,14 @@ export function promiseToCallback(promise: Promise<any>): 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);
});
promise.then(
(data: any) => {
cb(undefined, data);
},
(err: Error) => {
cb(err);
}
);
};
}
@ -200,11 +220,14 @@ export function promiseToServiceCallback<T>(promise: Promise<HttpOperationRespon
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);
});
promise.then(
(data: HttpOperationResponse) => {
process.nextTick(cb, undefined, data.parsedBody as T, data.request, data);
},
(err: Error) => {
process.nextTick(cb, err);
}
);
};
}
@ -221,8 +244,8 @@ export function prepareXMLRootList(obj: any, elementName: string) {
* @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 => {
sourceCtors.forEach((sourceCtors) => {
Object.getOwnPropertyNames(sourceCtors.prototype).forEach((name) => {
targetCtor.prototype[name] = sourceCtors.prototype[name];
});
});
@ -246,7 +269,11 @@ export function isDuration(value: string): boolean {
* @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 {
export function replaceAll(
value: string | undefined,
searchValue: string,
replaceValue: string
): string | undefined {
return !value || !searchValue ? value : value.split(searchValue).join(replaceValue || "");
}
@ -258,4 +285,4 @@ export function replaceAll(value: string | undefined, searchValue: string, repla
*/
export function isPrimitiveType(value: any): boolean {
return (typeof value !== "object" && typeof value !== "function") || value === null;
}
}

Просмотреть файл

@ -16,7 +16,8 @@ export function parseXML(str: string): Promise<any> {
let errorNS = "";
try {
errorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0].namespaceURI!;
errorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0]
.namespaceURI!;
} catch (ignored) {
// Most browsers will return a document containing <parsererror>, but IE will throw.
}
@ -48,7 +49,12 @@ function domToObject(node: Node): 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 onlyChildTextValue: string | undefined =
(firstChildNode &&
childNodeCount === 1 &&
firstChildNode.nodeType === Node.TEXT_NODE &&
firstChildNode.nodeValue) ||
undefined;
const elementWithAttributes: Element | undefined = asElementWithAttributes(node);
if (elementWithAttributes) {
@ -93,12 +99,14 @@ 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 rootName = (opts && opts.rootName) || "root";
const dom = buildNode(obj, rootName)[0];
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + serializer.serializeToString(dom);
return (
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + serializer.serializeToString(dom)
);
}
function buildAttributes(attrs: { [key: string]: { toString(): string; } }): Attr[] {
function buildAttributes(attrs: { [key: string]: { toString(): string } }): Attr[] {
const result = [];
for (const key of Object.keys(attrs)) {
const attr = doc.createAttribute(key);
@ -113,8 +121,7 @@ function buildNode(obj: any, elementName: string): Node[] {
const elem = doc.createElement(elementName);
elem.textContent = obj.toString();
return [elem];
}
else if (Array.isArray(obj)) {
} else if (Array.isArray(obj)) {
const result = [];
for (const arrayElem of obj) {
for (const child of buildNode(arrayElem, elementName)) {
@ -136,8 +143,7 @@ function buildNode(obj: any, elementName: string): Node[] {
}
}
return [elem];
}
else {
} else {
throw new Error(`Illegal value passed to buildObject: ${obj}`);
}
}

Просмотреть файл

@ -7,8 +7,8 @@ export function stringifyXML(obj: any, opts?: { rootName?: string }) {
const builder = new xml2js.Builder({
rootName: (opts || {}).rootName,
renderOpts: {
pretty: false
}
pretty: false,
},
});
return builder.buildObject(obj);
}
@ -17,7 +17,7 @@ export function parseXML(str: string): Promise<any> {
const xmlParser = new xml2js.Parser({
explicitArray: false,
explicitCharkey: false,
explicitRoot: false
explicitRoot: false,
});
return new Promise((resolve, reject) => {
if (!str) {

Просмотреть файл

@ -9,8 +9,21 @@ 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);
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.
@ -19,7 +32,7 @@ export type TransferProgressEvent = {
/**
* The number of bytes loaded so far.
*/
loadedBytes: number
loadedBytes: number;
};
/**
@ -30,8 +43,16 @@ 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;
addEventListener: (
type: "abort",
listener: (this: AbortSignalLike, ev: Event) => any,
options?: any
) => void;
removeEventListener: (
type: "abort",
listener: (this: AbortSignalLike, ev: Event) => any,
options?: any
) => void;
}
/**
@ -178,9 +199,12 @@ export class WebResource {
* HttpOperationResponse combination. If this is undefined, then a simple status code lookup will
* be used.
*/
operationResponseGetter?: (operationSpec: OperationSpec, response: HttpOperationResponse) => (undefined | OperationResponse);
operationResponseGetter?: (
operationSpec: OperationSpec,
response: HttpOperationResponse
) => undefined | OperationResponse;
formData?: any;
query?: { [key: string]: any; };
query?: { [key: string]: any };
operationSpec?: OperationSpec;
withCredentials: boolean;
timeout: number;
@ -200,8 +224,8 @@ export class WebResource {
url?: string,
method?: HttpMethods,
body?: any,
query?: { [key: string]: any; },
headers?: { [key: string]: any; } | HttpHeadersLike,
query?: { [key: string]: any },
headers?: { [key: string]: any } | HttpHeadersLike,
streamResponseBody?: boolean,
withCredentials?: boolean,
abortSignal?: AbortSignalLike,
@ -210,12 +234,12 @@ export class WebResource {
onDownloadProgress?: (progress: TransferProgressEvent) => void,
proxySettings?: ProxySettings,
keepAlive?: boolean,
agentSettings?: AgentSettings) {
agentSettings?: AgentSettings
) {
this.streamResponseBody = streamResponseBody;
this.url = url || "";
this.method = method || "GET";
this.headers = (isHttpHeadersLike(headers) ? headers : new HttpHeaders(headers));
this.headers = isHttpHeadersLike(headers) ? headers : new HttpHeaders(headers);
this.body = body;
this.query = query;
this.formData = undefined;
@ -258,18 +282,22 @@ export class WebResource {
}
if (options.url && options.pathTemplate) {
throw new Error("options.url and options.pathTemplate are mutually exclusive. Please provide exactly one of them.");
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")) {
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\".");
throw new Error('options.url must be of type "string".');
}
this.url = options.url;
}
@ -278,35 +306,55 @@ export class WebResource {
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));
throw new Error(
'The provided method "' +
options.method +
'" is invalid. Supported HTTP methods are: ' +
JSON.stringify(validMethods)
);
}
}
this.method = (options.method.toUpperCase() as HttpMethods);
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\".");
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*})/ig);
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.`);
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 (
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") {
@ -315,7 +363,9 @@ export class WebResource {
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.`);
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);
@ -332,9 +382,11 @@ export class WebResource {
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 }.`);
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) {
@ -350,10 +402,11 @@ export class WebResource {
if (typeof queryParam === "string") {
queryParams.push(queryParamName + "=" + encodeURIComponent(queryParam));
this.query[queryParamName] = encodeURIComponent(queryParam);
}
else if (typeof queryParam === "object") {
} 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.`);
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);
@ -364,7 +417,7 @@ export class WebResource {
}
}
}
}// end-of-for
} // end-of-for
// append the queryString
this.url += queryParams.join("&");
}
@ -403,7 +456,11 @@ export class WebResource {
}
} else {
if (options.serializationMapper) {
this.body = new Serializer(options.mappers).serialize(options.serializationMapper, options.body, "requestBody");
this.body = new Serializer(options.mappers).serialize(
options.serializationMapper,
options.body,
"requestBody"
);
}
if (!options.disableJsonStringifyOnBody) {
this.body = JSON.stringify(options.body);
@ -437,7 +494,8 @@ export class WebResource {
this.onDownloadProgress,
this.proxySettings,
this.keepAlive,
this.agentSettings);
this.agentSettings
);
if (this.formData) {
result.formData = this.formData;

Просмотреть файл

@ -94,7 +94,7 @@ export class XhrHttpClient implements HttpClient {
request,
status: xhr.status,
headers: parseHeaders(xhr),
blobBody
blobBody,
});
}
});
@ -102,30 +102,40 @@ export class XhrHttpClient implements HttpClient {
});
} else {
return new Promise(function (resolve, reject) {
xhr.addEventListener("load", () => resolve({
request,
status: xhr.status,
headers: parseHeaders(xhr),
bodyAsText: xhr.responseText
}));
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) {
function addProgressListener(
xhr: XMLHttpRequestEventTarget,
listener?: (progress: TransferProgressEvent) => void
) {
if (listener) {
xhr.addEventListener("progress", rawEvent => listener({
loadedBytes: rawEvent.loaded
}));
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]+/);
const headerLines = xhr
.getAllResponseHeaders()
.trim()
.split(/[\r\n]+/);
for (const line of headerLines) {
const index = line.indexOf(":");
const headerName = line.slice(0, index);
@ -135,8 +145,34 @@ export function parseHeaders(xhr: XMLHttpRequest) {
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)));
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
)
)
);
}

Просмотреть файл

@ -5,7 +5,7 @@
"email": "azsdkteam@microsoft.com",
"url": "https://github.com/Azure/ms-rest-js"
},
"version": "2.2.0",
"version": "2.2.1",
"description": "Isomorphic client Runtime for Typescript/node.js/browser javascript client libraries generated using AutoRest",
"tags": [
"isomorphic",
@ -100,6 +100,7 @@
"mocha-multi-reporters": "^1.1.7",
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"prettier": "2.2.1",
"rollup": "^1.16.6",
"rollup-plugin-commonjs": "^10.0.1",
"rollup-plugin-json": "^4.0.0",
@ -139,6 +140,7 @@
"build:rollup": "rollup -c rollup.config.ts",
"build:minify-browser": "terser -c -m --comments --source-map \"content='./dist/msRest.browser.js.map'\" -o ./dist/msRest.browser.min.js ./dist/msRest.browser.js",
"build:test-browser": "webpack --config webpack.testconfig.ts",
"format": "prettier --write \"./**/*.ts\"",
"test": "run-p test:tslint test:unit test:karma",
"test:tslint": "tslint -p .",
"test:unit": "nyc mocha",

Просмотреть файл

@ -37,7 +37,7 @@ const nodeConfig = {
file: "./dist/msRest.node.js",
format: "cjs",
sourcemap: true,
banner
banner,
},
plugins: [
nodeResolve({
@ -48,9 +48,9 @@ const nodeConfig = {
json(),
visualizer({
filename: "dist/node-stats.html",
sourcemap: true
})
]
sourcemap: true,
}),
],
};
/**
@ -64,19 +64,19 @@ const browserConfig = {
format: "umd",
name: "msRest",
sourcemap: true,
banner
banner,
},
plugins: [
nodeResolve({
mainFields: ["module", "main", "browser"]
mainFields: ["module", "main", "browser"],
}),
commonjs(),
sourcemaps(),
visualizer({
filename: "dist/browser-stats.html",
sourcemap: true
})
]
sourcemap: true,
}),
],
};
export default [nodeConfig, browserConfig];

Просмотреть файл

@ -3,10 +3,9 @@
<head>
<title>My Todos</title>
<script type="text/javascript" src="../msRestBundle.js"></script>
<script type="text/javascript" src="../dist/msRest.browser.js"></script>
<script type="text/javascript">
document.write('hello world');
const msRest = className;
const subscriptionId = "00977cdb-163f-435f-9c32-39ec8ae61f4d";
const token = "token";
const creds = new msRest.TokenCredentials(token);

Просмотреть файл

@ -2,27 +2,24 @@
import * as msRest from "../lib/msRest";
const clientOptions: msRest.ServiceClientOptions = {
requestPolicyFactories: [msRest.logPolicy()]
// add log policy to list of default factories.
requestPolicyFactories: (factories) => factories.concat([msRest.logPolicy()]),
};
const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"] || "subscriptionId";
// An easy way to get the token
// 1. Go to this test drive link https://azure.github.io/projects/apis and authenticate by clicking on Authorize. Check the user impersoantion checkbox in the popup.
// 1.1 select a subscription of your choice
// 1.2 select the storage-2015-06-15 option from the first drop down list
// 1.3 expand the url to list storage accounts in a subscription
// 1.4 click on try it out button.
// 1.5 in the curl tab you will see the actual curl request that has the bearer token in it
// 1.6 copy paste that token here. That token is valid for 1 hour
// An easy way to get the token using Azure CLI (https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
// 1. `az login` using the above subscription
// 2. `az account set -s <subscription id>`
// 3. `az account get-access-token --resource=https://management.azure.com`
// 4. copy paste that token here. That token is valid for 1 hour
const token = process.env["ACCESS_TOKEN"] || "token";
const creds = new msRest.TokenCredentials(token);
const client = new msRest.ServiceClient(creds, clientOptions);
const req: msRest.RequestPrepareOptions = {
url: `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.Storage/storageAccounts?api-version=2015-06-15`,
method: "GET"
method: "GET",
};
client.sendRequest(req).then(function (res: msRest.HttpOperationResponse) {
console.log(res.bodyAsText);
});

Просмотреть файл

@ -20,7 +20,9 @@ describe("Token credentials", () => {
creds.signRequest(request).then((signedRequest: msRest.WebResourceLike) => {
signedRequest.headers.get("authorization")!.should.exist;
signedRequest.headers.get("authorization")!.should.match(new RegExp("^Bearer\\s+" + dummyToken + "$"));
signedRequest.headers
.get("authorization")!
.should.match(new RegExp("^Bearer\\s+" + dummyToken + "$"));
done();
});
});
@ -30,14 +32,15 @@ describe("Token credentials", () => {
const request = new msRest.WebResource();
creds.signRequest(request).then((signedRequest: msRest.WebResourceLike) => {
signedRequest.headers.get("authorization")!.should.exist;
signedRequest.headers.get("authorization")!.should.match(new RegExp("^" + fakeScheme + "\\s+" + dummyToken + "$"));
signedRequest.headers
.get("authorization")!
.should.match(new RegExp("^" + fakeScheme + "\\s+" + dummyToken + "$"));
done();
});
});
});
describe("construction", () => {
it("should succeed with token", () => {
(() => {
new TokenCredentials(dummyToken);
@ -66,7 +69,9 @@ describe("Basic Authentication credentials", () => {
const request = new msRest.WebResource();
creds.signRequest(request).then((signedRequest: msRest.WebResourceLike) => {
signedRequest.headers.get("authorization")!.should.exist;
signedRequest.headers.get("authorization")!.should.match(new RegExp("^Basic\\s+" + encodedCredentials + "$"));
signedRequest.headers
.get("authorization")!
.should.match(new RegExp("^Basic\\s+" + encodedCredentials + "$"));
done();
});
});
@ -77,7 +82,9 @@ describe("Basic Authentication credentials", () => {
creds.signRequest(request).then((signedRequest: msRest.WebResourceLike) => {
signedRequest.headers.get("authorization")!.should.exist;
signedRequest.headers.get("authorization")!.should.match(new RegExp("^" + fakeScheme + "\\s+" + encodedCredentials + "$"));
signedRequest.headers
.get("authorization")!
.should.match(new RegExp("^" + fakeScheme + "\\s+" + encodedCredentials + "$"));
done();
});
});
@ -94,7 +101,7 @@ describe("Basic Authentication credentials", () => {
describe("ApiKey credentials", () => {
describe("usage", function () {
it("should set header parameters properly in request", async function () {
const creds = new ApiKeyCredentials({inHeader: {"key1": "value1", "key2": "value2"}});
const creds = new ApiKeyCredentials({ inHeader: { key1: "value1", key2: "value2" } });
const request = new msRest.WebResource();
request.headers = new msRest.HttpHeaders();
@ -107,10 +114,10 @@ describe("Basic Authentication credentials", () => {
});
it("should set query parameters properly in the request url without any query parameters", async function () {
const creds = new ApiKeyCredentials({inQuery: {"key1": "value1", "key2": "value2"}});
const creds = new ApiKeyCredentials({ inQuery: { key1: "value1", key2: "value2" } });
const request = {
headers: {},
url: "https://example.com"
url: "https://example.com",
} as msRest.WebResource;
await creds.signRequest(request);
@ -118,10 +125,10 @@ describe("Basic Authentication credentials", () => {
});
it("should set query parameters properly in the request url with existing query parameters", async function () {
const creds = new ApiKeyCredentials({inQuery: {"key1": "value1", "key2": "value2"}});
const creds = new ApiKeyCredentials({ inQuery: { key1: "value1", key2: "value2" } });
const request = {
headers: {},
url: "https://example.com?q1=v2"
url: "https://example.com?q1=v2",
} as msRest.WebResource;
await creds.signRequest(request);
@ -130,25 +137,24 @@ describe("Basic Authentication credentials", () => {
});
describe("construction", function () {
it("should fail with options.inHeader and options.inQuery set to null or undefined", function (done) {
(function () {
new ApiKeyCredentials({ inHeader: undefined, inQuery: undefined } as any);
}).should.throw();
}.should.throw());
done();
});
it("should fail without options", function (done) {
(function () {
new (ApiKeyCredentials as any)();
}).should.throw();
}.should.throw());
done();
});
it("should fail with empty options", function (done) {
(function () {
new ApiKeyCredentials({});
}).should.throw();
}.should.throw());
done();
});
});

Просмотреть файл

@ -16,29 +16,29 @@ internalMappers.Cat = {
required: false,
serializedName: "id",
type: {
name: "Number"
}
name: "Number",
},
},
name: {
required: false,
serializedName: "name",
type: {
name: "String"
}
name: "String",
},
},
pettype: {
required: true,
serializedName: "pet\\.type",
type: {
name: "String"
}
name: "String",
},
},
color: {
required: false,
serializedName: "color",
type: {
name: "String"
}
name: "String",
},
},
hates: {
required: false,
@ -50,13 +50,13 @@ internalMappers.Cat = {
serializedName: "DogElementType",
type: {
name: "Composite",
className: "Dog"
}
}
}
}
}
}
className: "Dog",
},
},
},
},
},
},
};
internalMappers.Dog = {
required: false,
@ -69,32 +69,32 @@ internalMappers.Dog = {
required: false,
serializedName: "id",
type: {
name: "Number"
}
name: "Number",
},
},
name: {
required: false,
serializedName: "name",
type: {
name: "String"
}
name: "String",
},
},
pettype: {
required: true,
serializedName: "pet\\.type",
type: {
name: "String"
}
name: "String",
},
},
food: {
required: false,
serializedName: "food",
type: {
name: "String"
}
}
}
}
name: "String",
},
},
},
},
};
internalMappers.Fish = {
required: false,
@ -103,7 +103,7 @@ internalMappers.Fish = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Fish",
@ -112,15 +112,15 @@ internalMappers.Fish = {
required: false,
serializedName: "species",
type: {
name: "String"
}
name: "String",
},
},
length: {
required: true,
serializedName: "length",
type: {
name: "Number"
}
name: "Number",
},
},
siblings: {
required: false,
@ -134,23 +134,23 @@ internalMappers.Fish = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Fish"
}
}
}
className: "Fish",
},
},
},
},
fishtype: {
required: true,
serializedName: "fish\\.type",
type: {
name: "String"
}
}
}
}
name: "String",
},
},
},
},
};
internalMappers.Invoice = {
required: false,
@ -163,15 +163,15 @@ internalMappers.Invoice = {
serializedName: "invoiceId",
required: true,
type: {
name: "Number"
}
name: "Number",
},
},
invDate: {
serializedName: "invDate",
required: false,
type: {
name: "Date"
}
name: "Date",
},
},
invProducts: {
serializedName: "invProducts",
@ -184,15 +184,15 @@ internalMappers.Invoice = {
value: {
type: {
name: "Composite",
className: "Product"
}
}
}
}
}
}
}
}
className: "Product",
},
},
},
},
},
},
},
},
};
internalMappers.Pet = {
required: false,
@ -206,25 +206,25 @@ internalMappers.Pet = {
required: false,
serializedName: "id",
type: {
name: "Number"
}
name: "Number",
},
},
name: {
required: false,
serializedName: "name",
type: {
name: "String"
}
name: "String",
},
},
pettype: {
required: true,
serializedName: "pet\\.type",
type: {
name: "String"
}
}
}
}
name: "String",
},
},
},
},
};
internalMappers.PetAP = {
required: false,
@ -233,8 +233,8 @@ internalMappers.PetAP = {
name: "Composite",
additionalProperties: {
type: {
name: "String"
}
name: "String",
},
},
className: "PetAP",
modelProperties: {
@ -242,15 +242,15 @@ internalMappers.PetAP = {
required: true,
serializedName: "id",
type: {
name: "Number"
}
name: "Number",
},
},
name: {
required: false,
serializedName: "name",
type: {
name: "String"
}
name: "String",
},
},
eyeColor: {
required: true,
@ -258,31 +258,31 @@ internalMappers.PetAP = {
isConstant: true,
defaultValue: "brown",
type: {
name: "String"
}
name: "String",
},
},
favoriteFood: {
required: false,
serializedName: "favoriteFood",
defaultValue: "bones",
type: {
name: "String"
}
name: "String",
},
},
status: {
required: false,
readOnly: true,
serializedName: "status",
type: {
name: "Boolean"
}
name: "Boolean",
},
},
odatalocation: {
required: true,
serializedName: "@odata\\.location",
type: {
name: "String"
}
name: "String",
},
},
additionalProperties1: {
required: false,
@ -293,13 +293,13 @@ internalMappers.PetAP = {
required: false,
serializedName: "NumberElementType",
type: {
name: "Number"
}
}
}
}
}
}
name: "Number",
},
},
},
},
},
},
};
internalMappers.PetGallery = {
required: false,
@ -312,15 +312,15 @@ internalMappers.PetGallery = {
required: false,
serializedName: "id",
type: {
name: "Number"
}
name: "Number",
},
},
name: {
required: false,
serializedName: "name",
type: {
name: "String"
}
name: "String",
},
},
pets: {
required: false,
@ -334,13 +334,13 @@ internalMappers.PetGallery = {
name: "Composite",
polymorphicDiscriminator: "pet.type",
uberParent: "Pet",
className: "Pet"
}
}
}
}
}
}
className: "Pet",
},
},
},
},
},
},
};
internalMappers.Product = {
required: false,
@ -351,33 +351,31 @@ internalMappers.Product = {
modelProperties: {
id: {
serializedName: "id",
constraints: {
},
constraints: {},
required: true,
type: {
name: "Number"
}
name: "Number",
},
},
name: {
serializedName: "name",
required: true,
type: {
name: "String"
name: "String",
},
constraints: {
MaxLength: 256,
MinLength: 1,
Pattern: /^[A-Za-z0-9-._]+$/
}
Pattern: /^[A-Za-z0-9-._]+$/,
},
},
provisioningState: {
serializedName: "properties.provisioningState",
required: false,
type: {
name: "Enum",
allowedValues: ["Creating", "Failed", "Succeeded"]
}
allowedValues: ["Creating", "Failed", "Succeeded"],
},
},
tags: {
serializedName: "tags",
@ -386,25 +384,25 @@ internalMappers.Product = {
name: "Dictionary",
value: {
type: {
name: "String"
}
}
}
name: "String",
},
},
},
},
dispatchTime: {
serializedName: "dispatchTime",
required: false,
type: {
name: "DateTime"
}
name: "DateTime",
},
},
invoiceInfo: {
serializedName: "invoiceInfo",
required: false,
type: {
name: "Composite",
className: "Invoice"
}
className: "Invoice",
},
},
subProducts: {
serializedName: "subProducts",
@ -414,13 +412,13 @@ internalMappers.Product = {
element: {
type: {
name: "Composite",
className: "SubProduct"
}
}
}
}
}
}
className: "SubProduct",
},
},
},
},
},
},
};
internalMappers.ProductListResult = {
required: false,
@ -437,13 +435,13 @@ internalMappers.ProductListResult = {
element: {
type: {
name: "Composite",
className: "Product"
}
}
}
}
}
}
className: "Product",
},
},
},
},
},
},
};
internalMappers.ProductListResultNextLink = {
required: false,
@ -460,20 +458,20 @@ internalMappers.ProductListResultNextLink = {
element: {
type: {
name: "Composite",
className: "Product"
}
}
}
className: "Product",
},
},
},
},
nextLink: {
serializedName: "nextLink",
required: false,
type: {
name: "String"
}
}
}
}
name: "String",
},
},
},
},
};
internalMappers.SawShark = {
required: false,
@ -482,7 +480,7 @@ internalMappers.SawShark = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Sawshark",
@ -491,15 +489,15 @@ internalMappers.SawShark = {
required: false,
serializedName: "species",
type: {
name: "String"
}
name: "String",
},
},
length: {
required: true,
serializedName: "length",
type: {
name: "Number"
}
name: "Number",
},
},
siblings: {
required: false,
@ -513,44 +511,44 @@ internalMappers.SawShark = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Fish"
}
}
}
className: "Fish",
},
},
},
},
fishtype: {
required: true,
serializedName: "fish\\.type",
type: {
name: "String"
}
name: "String",
},
},
age: {
required: false,
serializedName: "age",
type: {
name: "Number"
}
name: "Number",
},
},
birthday: {
required: true,
serializedName: "birthday",
type: {
name: "DateTime"
}
name: "DateTime",
},
},
picture: {
required: false,
serializedName: "picture",
type: {
name: "ByteArray"
}
}
}
}
name: "ByteArray",
},
},
},
},
};
internalMappers.Shark = {
required: false,
@ -559,7 +557,7 @@ internalMappers.Shark = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Shark",
@ -568,15 +566,15 @@ internalMappers.Shark = {
required: false,
serializedName: "species",
type: {
name: "String"
}
name: "String",
},
},
length: {
required: true,
serializedName: "length",
type: {
name: "Number"
}
name: "Number",
},
},
siblings: {
required: false,
@ -590,37 +588,37 @@ internalMappers.Shark = {
name: "Composite",
polymorphicDiscriminator: {
serializedName: "fish.type",
clientName: "fishtype"
clientName: "fishtype",
},
uberParent: "Fish",
className: "Fish"
}
}
}
className: "Fish",
},
},
},
},
fishtype: {
required: true,
serializedName: "fish\\.type",
type: {
name: "String"
}
name: "String",
},
},
age: {
required: false,
serializedName: "age",
type: {
name: "Number"
}
name: "Number",
},
},
birthday: {
required: true,
serializedName: "birthday",
type: {
name: "DateTime"
}
}
}
}
name: "DateTime",
},
},
},
},
};
internalMappers.SubProduct = {
required: false,
@ -633,50 +631,50 @@ internalMappers.SubProduct = {
serializedName: "subId",
required: true,
type: {
name: "Number"
}
name: "Number",
},
},
subName: {
serializedName: "subName",
required: true,
type: {
name: "String"
}
name: "String",
},
},
provisioningState: {
serializedName: "provisioningState",
required: false,
type: {
name: "Enum",
allowedValues: ["Creating", "Failed", "Succeeded"]
}
allowedValues: ["Creating", "Failed", "Succeeded"],
},
},
makeTime: {
serializedName: "makeTime",
required: false,
type: {
name: "DateTime"
}
name: "DateTime",
},
},
invoiceInfo: {
serializedName: "invoiceInfo",
required: false,
type: {
name: "Composite",
className: "Invoice"
}
}
}
}
className: "Invoice",
},
},
},
},
};
internalMappers.discriminators = {
"Fish": internalMappers.Fish,
Fish: internalMappers.Fish,
"Fish.shark": internalMappers.Shark,
"Fish.sawshark": internalMappers.SawShark,
"Pet": internalMappers.Pet,
Pet: internalMappers.Pet,
"Pet.Cat": internalMappers.Cat,
"Pet.Dog": internalMappers.Dog
"Pet.Dog": internalMappers.Dog,
};
export const Mappers = internalMappers;

Просмотреть файл

@ -21,7 +21,8 @@ function getAbortController(): AbortController {
if (typeof AbortController === "function") {
controller = new AbortController();
} else {
const AbortControllerPonyfill = require("abortcontroller-polyfill/dist/cjs-ponyfill").AbortController;
const AbortControllerPonyfill = require("abortcontroller-polyfill/dist/cjs-ponyfill")
.AbortController;
controller = new AbortControllerPonyfill();
}
return controller;
@ -29,7 +30,7 @@ function getAbortController(): AbortController {
describe("defaultHttpClient", function () {
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}
let httpMock: HttpMockFacade;
@ -53,7 +54,6 @@ describe("defaultHttpClient", function () {
return httpClient;
}
it("should return a response instead of throwing for awaited 404", async function () {
const resourceUrl = "/nonexistent";
@ -77,7 +77,16 @@ describe("defaultHttpClient", function () {
});
const controller = getAbortController();
const veryBigPayload = "very long string";
const request = new WebResource(resourceUrl, "POST", veryBigPayload, undefined, undefined, true, undefined, controller.signal);
const request = new WebResource(
resourceUrl,
"POST",
veryBigPayload,
undefined,
undefined,
true,
undefined,
controller.signal
);
const client = getMockedHttpClient();
const promise = client.sendRequest(request);
controller.abort();
@ -94,14 +103,14 @@ describe("defaultHttpClient", function () {
httpMock.get("http://my.fake.domain/set-cookie", {
status: 200,
headers: {
"Set-Cookie": "data=123456"
}
"Set-Cookie": "data=123456",
},
});
httpMock.get("http://my.fake.domain/cookie", async (_url, _method, _body, headers) => {
return {
status: 200,
headers: headers
headers: headers,
};
});
@ -115,7 +124,9 @@ describe("defaultHttpClient", function () {
const response2 = await client.sendRequest(request2);
response2.headers.get("Cookie")!.should.equal("data=123456");
const request3 = new WebResource("http://my.fake.domain/cookie", "GET", undefined, undefined, { Cookie: "data=abcdefg" });
const request3 = new WebResource("http://my.fake.domain/cookie", "GET", undefined, undefined, {
Cookie: "data=abcdefg",
});
const response3 = await client.sendRequest(request3);
response3.headers.get("Cookie")!.should.equal("data=abcdefg");
});
@ -130,11 +141,29 @@ describe("defaultHttpClient", function () {
const controller = getAbortController();
const buf = "Very large string";
const requests = [
new WebResource("/fileupload", "POST", buf, undefined, undefined, true, undefined, controller.signal),
new WebResource("/fileupload", "POST", buf, undefined, undefined, true, undefined, controller.signal)
new WebResource(
"/fileupload",
"POST",
buf,
undefined,
undefined,
true,
undefined,
controller.signal
),
new WebResource(
"/fileupload",
"POST",
buf,
undefined,
undefined,
true,
undefined,
controller.signal
),
];
const client = getMockedHttpClient();
const promises = requests.map(r => client.sendRequest(r));
const promises = requests.map((r) => client.sendRequest(r));
controller.abort();
// Ensure each promise is individually rejected
for (const promise of promises) {
@ -159,16 +188,30 @@ describe("defaultHttpClient", function () {
it("for simple bodies", async function () {
httpMock.post("/fileupload", async (_url, _method, _body) => {
return { status: 251, body: body.repeat(9).substring(0, 200), headers: { "Content-Length": "200" } };
return {
status: 251,
body: body.repeat(9).substring(0, 200),
headers: { "Content-Length": "200" },
};
});
const upload: Notified = { notified: false };
const download: Notified = { notified: false };
const body = "Very large string to upload";
const request = new WebResource("/fileupload", "POST", body, undefined, undefined, false, undefined, undefined, 0,
ev => listener(upload, ev),
ev => listener(download, ev));
const request = new WebResource(
"/fileupload",
"POST",
body,
undefined,
undefined,
false,
undefined,
undefined,
0,
(ev) => listener(upload, ev),
(ev) => listener(download, ev)
);
const client = getMockedHttpClient();
const response = await client.sendRequest(request);
@ -189,24 +232,38 @@ describe("defaultHttpClient", function () {
const size = isNode ? payload.toString().length : undefined;
httpMock.post("/bigfileupload", async (_url, _method, _body) => {
return { status: 250, body: payload, headers: { "Content-Type": "text/javascript", "Content-length": size } };
return {
status: 250,
body: payload,
headers: { "Content-Type": "text/javascript", "Content-length": size },
};
});
const upload: Notified = { notified: false };
const download: Notified = { notified: false };
const request = new WebResource("/bigfileupload", "POST", payload, undefined, undefined, true, undefined, undefined, 0,
ev => listener(upload, ev),
ev => listener(download, ev));
const request = new WebResource(
"/bigfileupload",
"POST",
payload,
undefined,
undefined,
true,
undefined,
undefined,
0,
(ev) => listener(upload, ev),
(ev) => listener(download, ev)
);
const client = getMockedHttpClient();
const response = await client.sendRequest(request);
response.status.should.equal(250);
if (response.blobBody) {
await response.blobBody;
} else if ((typeof response.readableStreamBody === "function")) {
} else if (typeof response.readableStreamBody === "function") {
const streamBody = (response.readableStreamBody as Function)();
streamBody.on("data", () => { });
streamBody.on("data", () => {});
await new Promise((resolve, reject) => {
streamBody.on("end", resolve);
streamBody.on("error", reject);
@ -221,7 +278,17 @@ describe("defaultHttpClient", function () {
it("should honor request timeouts", async function () {
httpMock.timeout("GET", "/slow");
const request = new WebResource("/slow", "GET", undefined, undefined, undefined, false, false, undefined, 100);
const request = new WebResource(
"/slow",
"GET",
undefined,
undefined,
undefined,
false,
false,
undefined,
100
);
const client = getMockedHttpClient();
try {
await client.sendRequest(request);
@ -251,12 +318,12 @@ describe("defaultHttpClient", function () {
httpMock.put(requestUrl, async (_url, _method, body, _headers) => {
if (!body) {
return {
status: 200
status: 200,
};
} else {
return {
status: 400,
body: `Expected empty body but got "${JSON.stringify(body)}"`
body: `Expected empty body but got "${JSON.stringify(body)}"`,
};
}
});
@ -282,8 +349,7 @@ describe("defaultHttpClient", function () {
assert(response.headers);
assert.strictEqual(response.headers.get("content-type")!.split(";")[0], "text/html");
const responseBody: string | null | undefined = response.bodyAsText;
const expectedResponseBody =
`<!doctype html>
const expectedResponseBody = `<!doctype html>
<html>
<head>
<title>Example Domain</title>
@ -332,7 +398,8 @@ describe("defaultHttpClient", function () {
`;
assert.strictEqual(
responseBody && responseBody.replace(/\s/g, ""),
expectedResponseBody.replace(/\s/g, ""));
expectedResponseBody.replace(/\s/g, "")
);
httpMock.teardown();
});
});

Просмотреть файл

@ -12,11 +12,10 @@ const emptyRequestPolicy: RequestPolicy = {
sendRequest(request: WebResource): Promise<HttpOperationResponse> {
// tslint:disable-next-line: no-null-keyword
return Promise.resolve({ request, status: 200, headers: new HttpHeaders(), bodyAsText: null });
}
},
};
describe("Log filter", () => {
it("should log messages when a logger object is provided", (done) => {
const expected = `>> Request: {
"url": "https://foo.com",
@ -34,17 +33,21 @@ describe("Log filter", () => {
>> Body: null
`;
let output = "";
const logger = (message: string): void => { output += message + "\n"; };
const logger = (message: string): void => {
output += message + "\n";
};
const lf = new LogPolicy(emptyRequestPolicy, new RequestPolicyOptions(), logger);
const req = new WebResource("https://foo.com", "PUT", { "a": 1 });
lf.sendRequest(req).then(() => {
// console.dir(output, { depth: null });
// console.log(">>>>>>>");
// console.dir(expected);
assert.deepEqual(output, expected);
done();
}).catch((err: Error) => {
done(err);
});
const req = new WebResource("https://foo.com", "PUT", { a: 1 });
lf.sendRequest(req)
.then(() => {
// console.dir(output, { depth: null });
// console.log(">>>>>>>");
// console.dir(expected);
assert.deepEqual(output, expected);
done();
})
.catch((err: Error) => {
done(err);
});
});
});

Просмотреть файл

@ -15,7 +15,12 @@ export type MockResponseData = {
headers?: any;
};
export type MockResponseFunction = (url?: string, method?: string, body?: any, headers?: any) => Promise<MockResponseData>;
export type MockResponseFunction = (
url?: string,
method?: string,
body?: any,
headers?: any
) => Promise<MockResponseData>;
export type MockResponse = MockResponseData | MockResponseFunction;
@ -32,7 +37,7 @@ export interface HttpMockFacade {
}
export function getHttpMock(): HttpMockFacade {
return (isNode ? new FetchHttpMock() : new BrowserHttpMock());
return isNode ? new FetchHttpMock() : new BrowserHttpMock();
}
class FetchHttpMock implements HttpMockFacade {
@ -43,7 +48,7 @@ class FetchHttpMock implements HttpMockFacade {
}
getFetch(): typeof node_fetch {
return this._fetch as unknown as typeof node_fetch;
return (this._fetch as unknown) as typeof node_fetch;
}
setup(): void {
@ -60,7 +65,7 @@ class FetchHttpMock implements HttpMockFacade {
timeout(_method: HttpMethods, url: UrlFilter): void {
const delay = new Promise((resolve) => {
setTimeout(() => resolve({$uri: url, delay: 500}), 2500);
setTimeout(() => resolve({ $uri: url, delay: 500 }), 2500);
});
this._fetch.mock(url, delay);
@ -94,7 +99,8 @@ class FetchHttpMock implements HttpMockFacade {
}) as fetch.MockResponseFunction;
}
const matcher = (_url: string, opts: fetch.MockRequest) => (url === _url) && (opts.method === method);
const matcher = (_url: string, opts: fetch.MockRequest) =>
url === _url && opts.method === method;
this._fetch.mock(matcher, mockResponse);
}
@ -123,13 +129,21 @@ export class BrowserHttpMock implements HttpMockFacade {
mockHttpMethod(method: HttpMethods, url: UrlFilter, response: MockResponse): void {
if (typeof response === "function") {
xhrMock.use(method, url, async (req, res) => {
const result = await response(req.url().toString(), req.method().toString(), req.body(), req.headers());
return res.status(result.status || 200).body(result.body || {}).headers(result.headers || {});
const result = await response(
req.url().toString(),
req.method().toString(),
req.body(),
req.headers()
);
return res
.status(result.status || 200)
.body(result.body || {})
.headers(result.headers || {});
});
} else {
xhrMock.use(method, url, {
status: response.status,
body: response.body
body: response.body,
});
}
}
@ -155,11 +169,10 @@ export class BrowserHttpMock implements HttpMockFacade {
}
timeout(method: HttpMethods, url: UrlFilter): void {
return this.mockHttpMethod(method, url, () => new Promise(() => { }));
return this.mockHttpMethod(method, url, () => new Promise(() => {}));
}
getFetch(): undefined {
return undefined;
}
}

Просмотреть файл

@ -5,10 +5,14 @@ import { assert } from "chai";
import { SuiteFunction, PendingSuiteFunction, TestFunction, PendingTestFunction } from "mocha";
import { isNode } from "../lib/util/utils";
export const nodeIt: TestFunction | PendingTestFunction = (!isNode ? it.skip : it);
export const browserIt: TestFunction | PendingTestFunction = (isNode ? it.skip : it);
export const nodeDescribe: SuiteFunction | PendingSuiteFunction = (!isNode ? describe.skip : describe);
export const browserDescribe: SuiteFunction | PendingSuiteFunction = (isNode ? describe.skip : describe);
export const nodeIt: TestFunction | PendingTestFunction = !isNode ? it.skip : it;
export const browserIt: TestFunction | PendingTestFunction = isNode ? it.skip : it;
export const nodeDescribe: SuiteFunction | PendingSuiteFunction = !isNode
? describe.skip
: describe;
export const browserDescribe: SuiteFunction | PendingSuiteFunction = isNode
? describe.skip
: describe;
/**
* Assert that the provided syncFunction throws an Error. If the expectedError is undefined, then
@ -17,7 +21,10 @@ export const browserDescribe: SuiteFunction | PendingSuiteFunction = (isNode ? d
* @param syncFunction The synchronous function that is expected to thrown an Error.
* @param expectedError The Error that is expected to be thrown.
*/
export function throws(syncFunction: () => void, expectedError?: ((error: Error) => void) | Error): Error {
export function throws(
syncFunction: () => void,
expectedError?: ((error: Error) => void) | Error
): Error {
let thrownError: Error | undefined;
try {
@ -27,7 +34,7 @@ export function throws(syncFunction: () => void, expectedError?: ((error: Error)
}
if (!thrownError) {
assert.throws(() => { });
assert.throws(() => {});
} else if (expectedError instanceof Error) {
assert.deepEqual(thrownError, expectedError);
} else if (expectedError) {
@ -44,7 +51,10 @@ export function throws(syncFunction: () => void, expectedError?: ((error: Error)
* @param asyncFunction The asynchronous function that is expected to thrown an Error.
* @param expectedError The Error that is expected to be thrown.
*/
export async function throwsAsync<T>(asyncFunction: (() => Promise<T>) | Promise<T>, expectedError?: ((error: Error) => void) | Error): Promise<Error> {
export async function throwsAsync<T>(
asyncFunction: (() => Promise<T>) | Promise<T>,
expectedError?: ((error: Error) => void) | Error
): Promise<Error> {
let thrownError: Error | undefined;
try {
@ -54,7 +64,7 @@ export async function throwsAsync<T>(asyncFunction: (() => Promise<T>) | Promise
}
if (!thrownError) {
assert.throws(() => { });
assert.throws(() => {});
} else if (expectedError instanceof Error) {
assert.deepEqual(thrownError, expectedError);
} else if (expectedError) {

Просмотреть файл

@ -16,7 +16,7 @@ const emptyRequestPolicy: RequestPolicy = {
sendRequest(request: WebResource): Promise<HttpOperationResponse> {
request.should.exist;
return Promise.resolve({ request: request, status: 200, headers: request.headers });
}
},
};
const getPlainUserAgentPolicy = (headerValue?: string): RequestPolicy => {
@ -96,18 +96,18 @@ describe("MsRestUserAgentPolicy", () => {
});
});
browserDescribe("for browser", function() {
browserDescribe("for browser", function () {
const userAgentHeaderKey = "x-ms-command-name";
const emptyRequestPolicy: RequestPolicy = {
sendRequest(request: WebResource): Promise<HttpOperationResponse> {
request.should.exist;
return Promise.resolve({ request: request, status: 200, headers: request.headers });
}
},
};
const getUserAgent = async (headerValue?: string): Promise<string> => {
const factory = userAgentPolicy({ value: headerValue});
const factory = userAgentPolicy({ value: headerValue });
const policy = factory.create(emptyRequestPolicy, new RequestPolicyOptions());
const resource = new WebResource();
await policy.sendRequest(resource);
@ -118,7 +118,10 @@ describe("MsRestUserAgentPolicy", () => {
describe("MsRestUserAgentPolicy (Browser)", () => {
it("should not modify user agent header if already present", async () => {
const factory = userAgentPolicy();
const browserUserAgentPolicy = factory.create(emptyRequestPolicy, new RequestPolicyOptions());
const browserUserAgentPolicy = factory.create(
emptyRequestPolicy,
new RequestPolicyOptions()
);
const customUserAgent = "my custom user agent";
const resource = new WebResource();
resource.headers.set(userAgentHeaderKey, customUserAgent);
@ -132,7 +135,10 @@ describe("MsRestUserAgentPolicy", () => {
it("should use injected user agent string if provided", async () => {
const customUserAgent = "my custom user agent";
const factory = userAgentPolicy({ value: customUserAgent });
const browserUserAgentPolicy = factory.create(emptyRequestPolicy, new RequestPolicyOptions());
const browserUserAgentPolicy = factory.create(
emptyRequestPolicy,
new RequestPolicyOptions()
);
const resource = new WebResource();
await browserUserAgentPolicy.sendRequest(resource);

Просмотреть файл

@ -20,9 +20,9 @@ describe("getParameterPathString()", () => {
mapper: {
serializedName: "value",
type: {
name: "Number"
}
}
name: "Number",
},
},
};
assert.strictEqual(getPathStringFromParameter(parameter), "pathToParameterValue");
});
@ -33,9 +33,9 @@ describe("getParameterPathString()", () => {
mapper: {
serializedName: "value",
type: {
name: "Number"
}
}
name: "Number",
},
},
};
assert.strictEqual(getPathStringFromParameter(parameter), "path.to.parameter.value");
});
@ -46,9 +46,9 @@ describe("getParameterPathString()", () => {
mapper: {
serializedName: "value",
type: {
name: "Number"
}
}
name: "Number",
},
},
};
assert.strictEqual(getPathStringFromParameter(parameter), "pa.th.to.par.ameter.valu.e");
});
@ -56,15 +56,15 @@ describe("getParameterPathString()", () => {
it("should return the mapper's serialized name when the parameterPath is an object", () => {
const parameter: OperationParameter = {
parameterPath: {
"a": "A",
"b": "B"
a: "A",
b: "B",
},
mapper: {
serializedName: "value",
type: {
name: "Number"
}
}
name: "Number",
},
},
};
assert.strictEqual(getPathStringFromParameter(parameter), "value");
});

Просмотреть файл

@ -33,30 +33,19 @@ describe("AgentPolicy", function () {
it("factory passes correct agent settings", function () {
const factory = agentPolicy(agentSettings);
const policy = factory.create(
emptyRequestPolicy,
emptyPolicyOptions
) as AgentPolicy;
const policy = factory.create(emptyRequestPolicy, emptyPolicyOptions) as AgentPolicy;
policy.agentSettings.should.be.deep.equal(agentSettings);
});
it("sets correct agent settings through constructor", function () {
const policy = new AgentPolicy(
emptyRequestPolicy,
emptyPolicyOptions,
agentSettings
);
const policy = new AgentPolicy(emptyRequestPolicy, emptyPolicyOptions, agentSettings);
policy.agentSettings.should.be.deep.equal(agentSettings);
});
it("should assign agent settings to the web request", async function () {
const policy = new AgentPolicy(
emptyRequestPolicy,
emptyPolicyOptions,
agentSettings
);
const policy = new AgentPolicy(emptyRequestPolicy, emptyPolicyOptions, agentSettings);
const request = new WebResource();
await policy.sendRequest(request);
@ -65,16 +54,12 @@ describe("AgentPolicy", function () {
});
it("should not override agent settings to the web request", async function () {
const policy = new AgentPolicy(
emptyRequestPolicy,
emptyPolicyOptions,
agentSettings
);
const policy = new AgentPolicy(emptyRequestPolicy, emptyPolicyOptions, agentSettings);
const request = new WebResource();
const requestSpecificAgentSettings = {
http: new http.Agent({keepAlive: true}),
https: new http.Agent({keepAlive: true}),
http: new http.Agent({ keepAlive: true }),
https: new http.Agent({ keepAlive: true }),
};
request.agentSettings = requestSpecificAgentSettings;

Просмотреть файл

@ -5,7 +5,13 @@ import { assert } from "chai";
import { HttpHeaders } from "../../lib/httpHeaders";
import { HttpOperationResponse } from "../../lib/httpOperationResponse";
import { HttpClient, OperationSpec, Serializer } from "../../lib/msRest";
import { DeserializationPolicy, deserializationPolicy, deserializeResponseBody, defaultJsonContentTypes, defaultXmlContentTypes } from "../../lib/policies/deserializationPolicy";
import {
DeserializationPolicy,
deserializationPolicy,
deserializeResponseBody,
defaultJsonContentTypes,
defaultXmlContentTypes,
} from "../../lib/policies/deserializationPolicy";
import { RequestPolicy, RequestPolicyOptions } from "../../lib/policies/requestPolicy";
import { WebResource, WebResourceLike } from "../../lib/webResource";
@ -15,13 +21,17 @@ describe("deserializationPolicy", function () {
return Promise.resolve({
request: request,
status: 200,
headers: new HttpHeaders()
headers: new HttpHeaders(),
});
}
},
};
it(`should not modify a request that has no request body mapper`, async function () {
const deserializationPolicy = new DeserializationPolicy(mockPolicy, {}, new RequestPolicyOptions());
const deserializationPolicy = new DeserializationPolicy(
mockPolicy,
{},
new RequestPolicyOptions()
);
const request = createRequest();
request.body = "hello there!";
@ -33,12 +43,13 @@ describe("deserializationPolicy", function () {
it("should parse a JSON response body", async function () {
const request: WebResourceLike = createRequest();
const mockClient: HttpClient = {
sendRequest: req => Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "application/json" }),
bodyAsText: "[123, 456, 789]"
})
sendRequest: (req) =>
Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "application/json" }),
bodyAsText: "[123, 456, 789]",
}),
};
const policy = deserializationPolicy().create(mockClient, new RequestPolicyOptions());
@ -49,12 +60,13 @@ describe("deserializationPolicy", function () {
it("should parse a JSON response body with a charset specified in Content-Type", async function () {
const request: WebResourceLike = createRequest();
const mockClient: HttpClient = {
sendRequest: req => Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "application/json;charset=UTF-8" }),
bodyAsText: "[123, 456, 789]"
})
sendRequest: (req) =>
Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "application/json;charset=UTF-8" }),
bodyAsText: "[123, 456, 789]",
}),
};
const policy = deserializationPolicy().create(mockClient, new RequestPolicyOptions());
@ -65,12 +77,13 @@ describe("deserializationPolicy", function () {
it("should parse a JSON response body with an uppercase Content-Type", async function () {
const request: WebResourceLike = createRequest();
const mockClient: HttpClient = {
sendRequest: req => Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "APPLICATION/JSON" }),
bodyAsText: "[123, 456, 789]"
})
sendRequest: (req) =>
Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders({ "Content-Type": "APPLICATION/JSON" }),
bodyAsText: "[123, 456, 789]",
}),
};
const policy = deserializationPolicy().create(mockClient, new RequestPolicyOptions());
@ -81,12 +94,13 @@ describe("deserializationPolicy", function () {
it("should parse a JSON response body with a missing Content-Type", async function () {
const request: WebResourceLike = createRequest();
const mockClient: HttpClient = {
sendRequest: req => Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders(),
bodyAsText: "[123, 456, 789]"
})
sendRequest: (req) =>
Promise.resolve({
request: req,
status: 200,
headers: new HttpHeaders(),
bodyAsText: "[123, 456, 789]",
}),
};
const policy = deserializationPolicy().create(mockClient, new RequestPolicyOptions());
@ -99,7 +113,7 @@ describe("deserializationPolicy", function () {
const response: HttpOperationResponse = {
request: createRequest(),
status: 200,
headers: new HttpHeaders()
headers: new HttpHeaders(),
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -117,9 +131,9 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "application/xml"
"content-type": "application/xml",
}),
bodyAsText: `<fruit><apples>3</apples></fruit>`
bodyAsText: `<fruit><apples>3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -128,7 +142,7 @@ describe("deserializationPolicy", function () {
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples>3</apples></fruit>`);
assert.deepEqual(deserializedResponse.parsedBody, { "apples": "3" });
assert.deepEqual(deserializedResponse.parsedBody, { apples: "3" });
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -137,9 +151,9 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "application/xml"
"content-type": "application/xml",
}),
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -147,14 +161,17 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples tasty="yes">3</apples></fruit>`);
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples tasty="yes">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, {
"apples": {
"$": {
"tasty": "yes"
apples: {
$: {
tasty: "yes",
},
"_": "3"
}
_: "3",
},
});
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -177,20 +194,20 @@ describe("deserializationPolicy", function () {
xmlName: "apples",
serializedName: "apples",
type: {
name: "String"
}
}
}
}
}
}
}
name: "String",
},
},
},
},
},
},
},
}),
status: 200,
headers: new HttpHeaders({
"content-type": "application/xml"
"content-type": "application/xml",
}),
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -198,8 +215,11 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples tasty="yes">3</apples></fruit>`);
assert.deepEqual(deserializedResponse.parsedBody, { "apples": "3" });
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples tasty="yes">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, { apples: "3" });
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -221,20 +241,20 @@ describe("deserializationPolicy", function () {
xmlName: "apples",
serializedName: "apples",
type: {
name: "Number"
}
}
}
}
}
}
}
name: "Number",
},
},
},
},
},
},
},
}),
status: 200,
headers: new HttpHeaders({
"content-type": "application/xml"
"content-type": "application/xml",
}),
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -242,8 +262,11 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples tasty="yes">3</apples></fruit>`);
assert.deepEqual(deserializedResponse.parsedBody, { "apples": 3 });
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples tasty="yes">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, { apples: 3 });
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -273,23 +296,23 @@ describe("deserializationPolicy", function () {
xmlIsAttribute: true,
serializedName: "tasty",
type: {
name: "String"
}
}
}
}
}
}
}
}
}
}
name: "String",
},
},
},
},
},
},
},
},
},
},
}),
status: 200,
headers: new HttpHeaders({
"content-type": "application/xml"
"content-type": "application/xml",
}),
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -297,8 +320,11 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples tasty="yes">3</apples></fruit>`);
assert.deepEqual(deserializedResponse.parsedBody, { "apples": { "tasty": "yes" } });
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples tasty="yes">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, { apples: { tasty: "yes" } });
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -307,9 +333,9 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "application/atom+xml"
"content-type": "application/atom+xml",
}),
bodyAsText: `<fruit><apples>3</apples></fruit>`
bodyAsText: `<fruit><apples>3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -318,7 +344,7 @@ describe("deserializationPolicy", function () {
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples>3</apples></fruit>`);
assert.deepEqual(deserializedResponse.parsedBody, { "apples": "3" });
assert.deepEqual(deserializedResponse.parsedBody, { apples: "3" });
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -327,9 +353,9 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "application/atom+xml"
"content-type": "application/atom+xml",
}),
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -337,14 +363,17 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples taste="good">3</apples></fruit>`);
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples taste="good">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, {
"apples": {
"$": {
"taste": "good"
apples: {
$: {
taste: "good",
},
"_": "3"
}
_: "3",
},
});
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -354,24 +383,31 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "my/weird-xml"
"content-type": "my/weird-xml",
}),
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponseBody([], ["my/weird-xml"], response);
const deserializedResponse: HttpOperationResponse = await deserializeResponseBody(
[],
["my/weird-xml"],
response
);
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<fruit><apples taste="good">3</apples></fruit>`);
assert.strictEqual(
deserializedResponse.bodyAsText,
`<fruit><apples taste="good">3</apples></fruit>`
);
assert.deepEqual(deserializedResponse.parsedBody, {
"apples": {
"$": {
"taste": "good"
apples: {
$: {
taste: "good",
},
"_": "3"
}
_: "3",
},
});
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});
@ -381,9 +417,9 @@ describe("deserializationPolicy", function () {
request: createRequest(),
status: 200,
headers: new HttpHeaders({
"content-type": "application/atom+xml;type=entry;charset=utf-8"
"content-type": "application/atom+xml;type=entry;charset=utf-8",
}),
bodyAsText: `<entry xmlns="http://www.w3.org/2005/Atom"><id>https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False</id><title type="text">testQueuePath</title><published>2018-10-09T19:56:34Z</published><updated>2018-10-09T19:56:35Z</updated><author><name>daschulttest1</name></author><link rel="self" href="https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False"/><content type="application/xml"><QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><LockDuration>PT1M</LockDuration><MaxSizeInMegabytes>1024</MaxSizeInMegabytes><RequiresDuplicateDetection>false</RequiresDuplicateDetection><RequiresSession>false</RequiresSession><DefaultMessageTimeToLive>P14D</DefaultMessageTimeToLive><DeadLetteringOnMessageExpiration>false</DeadLetteringOnMessageExpiration><DuplicateDetectionHistoryTimeWindow>PT10M</DuplicateDetectionHistoryTimeWindow><MaxDeliveryCount>10</MaxDeliveryCount><EnableBatchedOperations>true</EnableBatchedOperations><SizeInBytes>0</SizeInBytes><MessageCount>0</MessageCount><IsAnonymousAccessible>false</IsAnonymousAccessible><AuthorizationRules></AuthorizationRules><Status>Active</Status><CreatedAt>2018-10-09T19:56:34.903Z</CreatedAt><UpdatedAt>2018-10-09T19:56:35.013Z</UpdatedAt><AccessedAt>0001-01-01T00:00:00Z</AccessedAt><SupportOrdering>true</SupportOrdering><CountDetails xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus"><d2p1:ActiveMessageCount>0</d2p1:ActiveMessageCount><d2p1:DeadLetterMessageCount>0</d2p1:DeadLetterMessageCount><d2p1:ScheduledMessageCount>0</d2p1:ScheduledMessageCount><d2p1:TransferMessageCount>0</d2p1:TransferMessageCount><d2p1:TransferDeadLetterMessageCount>0</d2p1:TransferDeadLetterMessageCount></CountDetails><AutoDeleteOnIdle>P10675199DT2H48M5.4775807S</AutoDeleteOnIdle><EnablePartitioning>false</EnablePartitioning><EntityAvailabilityStatus>Available</EntityAvailabilityStatus><EnableExpress>false</EnableExpress></QueueDescription></content></entry>`
bodyAsText: `<entry xmlns="http://www.w3.org/2005/Atom"><id>https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False</id><title type="text">testQueuePath</title><published>2018-10-09T19:56:34Z</published><updated>2018-10-09T19:56:35Z</updated><author><name>daschulttest1</name></author><link rel="self" href="https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False"/><content type="application/xml"><QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><LockDuration>PT1M</LockDuration><MaxSizeInMegabytes>1024</MaxSizeInMegabytes><RequiresDuplicateDetection>false</RequiresDuplicateDetection><RequiresSession>false</RequiresSession><DefaultMessageTimeToLive>P14D</DefaultMessageTimeToLive><DeadLetteringOnMessageExpiration>false</DeadLetteringOnMessageExpiration><DuplicateDetectionHistoryTimeWindow>PT10M</DuplicateDetectionHistoryTimeWindow><MaxDeliveryCount>10</MaxDeliveryCount><EnableBatchedOperations>true</EnableBatchedOperations><SizeInBytes>0</SizeInBytes><MessageCount>0</MessageCount><IsAnonymousAccessible>false</IsAnonymousAccessible><AuthorizationRules></AuthorizationRules><Status>Active</Status><CreatedAt>2018-10-09T19:56:34.903Z</CreatedAt><UpdatedAt>2018-10-09T19:56:35.013Z</UpdatedAt><AccessedAt>0001-01-01T00:00:00Z</AccessedAt><SupportOrdering>true</SupportOrdering><CountDetails xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus"><d2p1:ActiveMessageCount>0</d2p1:ActiveMessageCount><d2p1:DeadLetterMessageCount>0</d2p1:DeadLetterMessageCount><d2p1:ScheduledMessageCount>0</d2p1:ScheduledMessageCount><d2p1:TransferMessageCount>0</d2p1:TransferMessageCount><d2p1:TransferDeadLetterMessageCount>0</d2p1:TransferDeadLetterMessageCount></CountDetails><AutoDeleteOnIdle>P10675199DT2H48M5.4775807S</AutoDeleteOnIdle><EnablePartitioning>false</EnablePartitioning><EntityAvailabilityStatus>Available</EntityAvailabilityStatus><EnableExpress>false</EnableExpress></QueueDescription></content></entry>`,
};
const deserializedResponse: HttpOperationResponse = await deserializeResponse(response);
@ -391,72 +427,77 @@ describe("deserializationPolicy", function () {
assert(deserializedResponse);
assert.strictEqual(deserializedResponse.readableStreamBody, undefined);
assert.strictEqual(deserializedResponse.blobBody, undefined);
assert.strictEqual(deserializedResponse.bodyAsText, `<entry xmlns="http://www.w3.org/2005/Atom"><id>https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False</id><title type="text">testQueuePath</title><published>2018-10-09T19:56:34Z</published><updated>2018-10-09T19:56:35Z</updated><author><name>daschulttest1</name></author><link rel="self" href="https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False"/><content type="application/xml"><QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><LockDuration>PT1M</LockDuration><MaxSizeInMegabytes>1024</MaxSizeInMegabytes><RequiresDuplicateDetection>false</RequiresDuplicateDetection><RequiresSession>false</RequiresSession><DefaultMessageTimeToLive>P14D</DefaultMessageTimeToLive><DeadLetteringOnMessageExpiration>false</DeadLetteringOnMessageExpiration><DuplicateDetectionHistoryTimeWindow>PT10M</DuplicateDetectionHistoryTimeWindow><MaxDeliveryCount>10</MaxDeliveryCount><EnableBatchedOperations>true</EnableBatchedOperations><SizeInBytes>0</SizeInBytes><MessageCount>0</MessageCount><IsAnonymousAccessible>false</IsAnonymousAccessible><AuthorizationRules></AuthorizationRules><Status>Active</Status><CreatedAt>2018-10-09T19:56:34.903Z</CreatedAt><UpdatedAt>2018-10-09T19:56:35.013Z</UpdatedAt><AccessedAt>0001-01-01T00:00:00Z</AccessedAt><SupportOrdering>true</SupportOrdering><CountDetails xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus"><d2p1:ActiveMessageCount>0</d2p1:ActiveMessageCount><d2p1:DeadLetterMessageCount>0</d2p1:DeadLetterMessageCount><d2p1:ScheduledMessageCount>0</d2p1:ScheduledMessageCount><d2p1:TransferMessageCount>0</d2p1:TransferMessageCount><d2p1:TransferDeadLetterMessageCount>0</d2p1:TransferDeadLetterMessageCount></CountDetails><AutoDeleteOnIdle>P10675199DT2H48M5.4775807S</AutoDeleteOnIdle><EnablePartitioning>false</EnablePartitioning><EntityAvailabilityStatus>Available</EntityAvailabilityStatus><EnableExpress>false</EnableExpress></QueueDescription></content></entry>`);
assert.strictEqual(
deserializedResponse.bodyAsText,
`<entry xmlns="http://www.w3.org/2005/Atom"><id>https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False</id><title type="text">testQueuePath</title><published>2018-10-09T19:56:34Z</published><updated>2018-10-09T19:56:35Z</updated><author><name>daschulttest1</name></author><link rel="self" href="https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&amp;enrich=False"/><content type="application/xml"><QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><LockDuration>PT1M</LockDuration><MaxSizeInMegabytes>1024</MaxSizeInMegabytes><RequiresDuplicateDetection>false</RequiresDuplicateDetection><RequiresSession>false</RequiresSession><DefaultMessageTimeToLive>P14D</DefaultMessageTimeToLive><DeadLetteringOnMessageExpiration>false</DeadLetteringOnMessageExpiration><DuplicateDetectionHistoryTimeWindow>PT10M</DuplicateDetectionHistoryTimeWindow><MaxDeliveryCount>10</MaxDeliveryCount><EnableBatchedOperations>true</EnableBatchedOperations><SizeInBytes>0</SizeInBytes><MessageCount>0</MessageCount><IsAnonymousAccessible>false</IsAnonymousAccessible><AuthorizationRules></AuthorizationRules><Status>Active</Status><CreatedAt>2018-10-09T19:56:34.903Z</CreatedAt><UpdatedAt>2018-10-09T19:56:35.013Z</UpdatedAt><AccessedAt>0001-01-01T00:00:00Z</AccessedAt><SupportOrdering>true</SupportOrdering><CountDetails xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus"><d2p1:ActiveMessageCount>0</d2p1:ActiveMessageCount><d2p1:DeadLetterMessageCount>0</d2p1:DeadLetterMessageCount><d2p1:ScheduledMessageCount>0</d2p1:ScheduledMessageCount><d2p1:TransferMessageCount>0</d2p1:TransferMessageCount><d2p1:TransferDeadLetterMessageCount>0</d2p1:TransferDeadLetterMessageCount></CountDetails><AutoDeleteOnIdle>P10675199DT2H48M5.4775807S</AutoDeleteOnIdle><EnablePartitioning>false</EnablePartitioning><EntityAvailabilityStatus>Available</EntityAvailabilityStatus><EnableExpress>false</EnableExpress></QueueDescription></content></entry>`
);
assert.deepEqual(deserializedResponse.parsedBody, {
"$": {
"xmlns": "http://www.w3.org/2005/Atom"
$: {
xmlns: "http://www.w3.org/2005/Atom",
},
"author": {
"name": "daschulttest1"
author: {
name: "daschulttest1",
},
"content": {
"$": {
"type": "application/xml"
content: {
$: {
type: "application/xml",
},
"QueueDescription": {
"$": {
"xmlns": "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect",
"xmlns:i": "http://www.w3.org/2001/XMLSchema-instance"
QueueDescription: {
$: {
xmlns: "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect",
"xmlns:i": "http://www.w3.org/2001/XMLSchema-instance",
},
"AccessedAt": "0001-01-01T00:00:00Z",
"AuthorizationRules": "",
"AutoDeleteOnIdle": "P10675199DT2H48M5.4775807S",
"CountDetails": {
"$": {
"xmlns:d2p1": "http://schemas.microsoft.com/netservices/2011/06/servicebus"
AccessedAt: "0001-01-01T00:00:00Z",
AuthorizationRules: "",
AutoDeleteOnIdle: "P10675199DT2H48M5.4775807S",
CountDetails: {
$: {
"xmlns:d2p1": "http://schemas.microsoft.com/netservices/2011/06/servicebus",
},
"d2p1:ActiveMessageCount": "0",
"d2p1:DeadLetterMessageCount": "0",
"d2p1:ScheduledMessageCount": "0",
"d2p1:TransferDeadLetterMessageCount": "0",
"d2p1:TransferMessageCount": "0"
"d2p1:TransferMessageCount": "0",
},
"CreatedAt": "2018-10-09T19:56:34.903Z",
"DeadLetteringOnMessageExpiration": "false",
"DefaultMessageTimeToLive": "P14D",
"DuplicateDetectionHistoryTimeWindow": "PT10M",
"EnableBatchedOperations": "true",
"EnableExpress": "false",
"EnablePartitioning": "false",
"EntityAvailabilityStatus": "Available",
"IsAnonymousAccessible": "false",
"LockDuration": "PT1M",
"MaxDeliveryCount": "10",
"MaxSizeInMegabytes": "1024",
"MessageCount": "0",
"RequiresDuplicateDetection": "false",
"RequiresSession": "false",
"SizeInBytes": "0",
"Status": "Active",
"SupportOrdering": "true",
"UpdatedAt": "2018-10-09T19:56:35.013Z"
}
},
"id": "https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False",
"link": {
"$": {
"href": "https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False",
"rel": "self"
}
},
"published": "2018-10-09T19:56:34Z",
"title": {
"$": {
"type": "text"
CreatedAt: "2018-10-09T19:56:34.903Z",
DeadLetteringOnMessageExpiration: "false",
DefaultMessageTimeToLive: "P14D",
DuplicateDetectionHistoryTimeWindow: "PT10M",
EnableBatchedOperations: "true",
EnableExpress: "false",
EnablePartitioning: "false",
EntityAvailabilityStatus: "Available",
IsAnonymousAccessible: "false",
LockDuration: "PT1M",
MaxDeliveryCount: "10",
MaxSizeInMegabytes: "1024",
MessageCount: "0",
RequiresDuplicateDetection: "false",
RequiresSession: "false",
SizeInBytes: "0",
Status: "Active",
SupportOrdering: "true",
UpdatedAt: "2018-10-09T19:56:35.013Z",
},
"_": "testQueuePath"
},
"updated": "2018-10-09T19:56:35Z"
id:
"https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False",
link: {
$: {
href:
"https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False",
rel: "self",
},
},
published: "2018-10-09T19:56:34Z",
title: {
$: {
type: "text",
},
_: "testQueuePath",
},
updated: "2018-10-09T19:56:35Z",
});
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});

Просмотреть файл

@ -16,15 +16,16 @@ describe("ProxyPolicy", function () {
host: "https://example.com",
port: 3030,
username: "admin",
password: "_password"
password: "_password",
};
const emptyRequestPolicy = {
sendRequest: (_: WebResourceLike) => Promise.resolve({
request: new WebResource(),
status: 404,
headers: new HttpHeaders(undefined)
})
sendRequest: (_: WebResourceLike) =>
Promise.resolve({
request: new WebResource(),
status: 404,
headers: new HttpHeaders(undefined),
}),
};
const emptyPolicyOptions = new RequestPolicyOptions();
@ -38,7 +39,6 @@ describe("ProxyPolicy", function () {
done();
});
it("sets correct proxy settings through constructor", function (done) {
const policy = new ProxyPolicy(emptyRequestPolicy, emptyPolicyOptions, proxySettings);
policy.proxySettings.should.be.deep.equal(proxySettings);
@ -68,7 +68,8 @@ describe("ProxyPolicy", function () {
browserDescribe("for browser", () => {
it("should throw an Error while constructing object", () => {
const construct = () => new ProxyPolicy(emptyRequestPolicy, emptyPolicyOptions, proxySettings);
const construct = () =>
new ProxyPolicy(emptyRequestPolicy, emptyPolicyOptions, proxySettings);
construct.should.throw();
});
});
@ -122,9 +123,9 @@ describe("getDefaultProxySettings", () => {
describe("should prefer HTTPS proxy over HTTP proxy", () => {
[
{ name: "lower case", func: (envVar: string) => envVar.toLowerCase() },
{ name: "upper case", func: (envVar: string) => envVar.toUpperCase() }
].forEach(testCase => {
{ name: "lower case", func: (envVar: string) => envVar.toLowerCase() },
{ name: "upper case", func: (envVar: string) => envVar.toUpperCase() },
].forEach((testCase) => {
it(`with ${testCase.name}`, () => {
const httpProxy = "http://proxy.microsoft.com";
const httpsProxy = "https://proxy.azure.com";
@ -149,7 +150,7 @@ describe("getDefaultProxySettings", () => {
});
});
["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"].forEach(envVariableName => {
["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"].forEach((envVariableName) => {
it(`should should load setting from "${envVariableName}" environmental variable`, () => {
process.env[envVariableName] = proxyUrl;
const proxySettings: ProxySettings = getDefaultProxySettings()!;
@ -162,11 +163,13 @@ describe("getDefaultProxySettings", () => {
});
browserDescribe("for browser", () => {
[undefined, "http://proxy.microsoft.com", "https://proxy.azure.com:8080"].forEach(proxyUrl => {
it(`should return undefined for ${proxyUrl}`, () => {
const proxySettings = getDefaultProxySettings(proxyUrl);
should().not.exist(proxySettings);
});
});
[undefined, "http://proxy.microsoft.com", "https://proxy.azure.com:8080"].forEach(
(proxyUrl) => {
it(`should return undefined for ${proxyUrl}`, () => {
const proxySettings = getDefaultProxySettings(proxyUrl);
should().not.exist(proxySettings);
});
}
);
});
});

Просмотреть файл

@ -0,0 +1,174 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { assert } from "chai";
import { SystemErrorRetryPolicy } from "../../lib/policies/systemErrorRetryPolicy";
import { RetryError } from "../../lib/policies/systemErrorRetryPolicy";
import { WebResource } from "../../lib/webResource";
import { HttpOperationResponse } from "../../lib/httpOperationResponse";
import { HttpHeaders, RequestPolicyOptions } from "../../lib/msRest";
describe("SystemErrorRetryPolicy", () => {
class PassThroughPolicy {
constructor(private _response: HttpOperationResponse) {}
public sendRequest(request: WebResource): Promise<HttpOperationResponse> {
const response = {
...this._response,
request: request,
};
return Promise.resolve(response);
}
}
// throw error on first sendRequest()
class FailFirstRequestPolicy {
count = 0;
constructor(private _response: HttpOperationResponse, private errorCode: string) {}
public sendRequest(request: WebResource): Promise<HttpOperationResponse> {
if (this.count === 0) {
this.count++;
const error: RetryError = {
code: this.errorCode,
name: "RetryError",
message: `Error message for ${this.errorCode}`,
};
return Promise.reject(error);
}
const response = {
...this._response,
request: request,
};
return Promise.resolve(response);
}
}
const defaultResponse = {
status: 200,
request: new WebResource(),
headers: new HttpHeaders(),
};
function createDefaultSystemErrorRetryPolicy(
response?: HttpOperationResponse
): SystemErrorRetryPolicy {
if (!response) {
response = defaultResponse;
}
const passThroughPolicy = new PassThroughPolicy(response);
return new SystemErrorRetryPolicy(passThroughPolicy, new RequestPolicyOptions());
}
describe("sendRequest", () => {
it("should clone the request", async () => {
const request = new WebResource();
const nextPolicy = {
sendRequest: (requestToSend: WebResource): Promise<HttpOperationResponse> => {
assert(request !== requestToSend);
return Promise.resolve(defaultResponse);
},
};
const policy = new SystemErrorRetryPolicy(nextPolicy, new RequestPolicyOptions());
await policy.sendRequest(request);
});
it("should not modify the request", async () => {
const request = new WebResource();
request.url = "http://url";
request.method = "PATCH";
request.body = { someProperty: "someValue" };
request.headers = new HttpHeaders({ header: "abc" });
request.query = { q: "param" };
const policy = createDefaultSystemErrorRetryPolicy();
const response = await policy.sendRequest(request);
delete (response.request as any).requestId;
delete (request as any).requestId;
assert.deepEqual(response.request, request);
});
["ETIMEDOUT", "ESOCKETTIMEDOUT", "ECONNREFUSED", "ECONNRESET", "ENOENT"].forEach((code) => {
it(`should retry if the error code is ${code}`, async () => {
const request = new WebResource();
const mockResponse = {
status: 200,
headers: new HttpHeaders(),
request: request,
};
const faultyPolicy = new FailFirstRequestPolicy(mockResponse, code);
const policy = new SystemErrorRetryPolicy(
faultyPolicy,
new RequestPolicyOptions(),
3,
10,
10,
20
);
const response = await policy.sendRequest(request);
delete (request as any).requestId;
delete (response.request as any).requestId;
assert.deepEqual(response, mockResponse, "Expecting response matches after retrying");
});
});
it("should do nothing when error code is not one of the retriable errors", async () => {
const request = new WebResource();
const faultyPolicy = new FailFirstRequestPolicy(defaultResponse, "NonRetriableError");
const policy = new SystemErrorRetryPolicy(
faultyPolicy,
new RequestPolicyOptions(),
3,
10,
10,
20
);
try {
await policy.sendRequest(request);
assert.fail("Expecting that an error has been thrown");
} catch (err) {
assert.equal((err as Error).message, "Error message for NonRetriableError");
}
});
["ETIMEDOUT", "ESOCKETTIMEDOUT", "ECONNREFUSED", "ECONNRESET", "ENOENT"].forEach((code) => {
it(`should fail after max retry count for error code ${code}`, async () => {
class FailEveryRequestPolicy {
count = 0;
constructor(private errorCode: string) {}
public sendRequest(_request: WebResource): Promise<HttpOperationResponse> {
const error: RetryError = {
code: this.errorCode,
name: "RetryError",
message: `Error message for ${this.errorCode}`,
};
return Promise.reject(error);
}
}
const request = new WebResource();
const faultyPolicy = new FailEveryRequestPolicy(code);
const policy = new SystemErrorRetryPolicy(
faultyPolicy,
new RequestPolicyOptions(),
3,
10,
10,
20
);
try {
await policy.sendRequest(request);
assert.fail("Expecting that an error has been thrown");
} catch (err) {
assert.equal((err as Error).message, `Error message for ${code}`);
}
});
});
});
}).timeout(60000);

Просмотреть файл

@ -10,11 +10,11 @@ import { HttpHeaders, RequestPolicyOptions } from "../../lib/msRest";
describe("ThrottlingRetryPolicy", () => {
class PassThroughPolicy {
constructor(private _response: HttpOperationResponse) { }
constructor(private _response: HttpOperationResponse) {}
public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
const response = {
...this._response,
request: request
request: request,
};
return Promise.resolve(response);
@ -24,10 +24,16 @@ describe("ThrottlingRetryPolicy", () => {
const defaultResponse = {
status: 200,
request: new WebResource(),
headers: new HttpHeaders()
headers: new HttpHeaders(),
};
function createDefaultThrottlingRetryPolicy(response?: HttpOperationResponse, actionHandler?: (httpRequest: WebResourceLike, response: HttpOperationResponse) => Promise<HttpOperationResponse>) {
function createDefaultThrottlingRetryPolicy(
response?: HttpOperationResponse,
actionHandler?: (
httpRequest: WebResourceLike,
response: HttpOperationResponse
) => Promise<HttpOperationResponse>
) {
if (!response) {
response = defaultResponse;
}
@ -43,7 +49,7 @@ describe("ThrottlingRetryPolicy", () => {
sendRequest: (requestToSend: WebResourceLike): Promise<HttpOperationResponse> => {
assert(request !== requestToSend);
return Promise.resolve(defaultResponse);
}
},
};
const policy = new ThrottlingRetryPolicy(nextPolicy, new RequestPolicyOptions());
await policy.sendRequest(request);
@ -54,7 +60,7 @@ describe("ThrottlingRetryPolicy", () => {
request.url = "http://url";
request.method = "PATCH";
request.body = { someProperty: "someValue" };
request.headers = new HttpHeaders({ "header": "abc" });
request.headers = new HttpHeaders({ header: "abc" });
request.query = { q: "param" };
const policy = createDefaultThrottlingRetryPolicy();
@ -68,11 +74,13 @@ describe("ThrottlingRetryPolicy", () => {
const mockResponse = {
status: 400,
headers: new HttpHeaders({
"Retry-After": "100"
"Retry-After": "100",
}),
request: request
request: request,
};
const policy = createDefaultThrottlingRetryPolicy(mockResponse, _ => { throw new AssertionError("fail"); });
const policy = createDefaultThrottlingRetryPolicy(mockResponse, (_) => {
throw new AssertionError("fail");
});
const response = await policy.sendRequest(request);
@ -84,9 +92,9 @@ describe("ThrottlingRetryPolicy", () => {
const mockResponse = {
status: 429,
headers: new HttpHeaders({
"Retry-After": "100"
"Retry-After": "100",
}),
request: request
request: request,
};
const policy = createDefaultThrottlingRetryPolicy(mockResponse, (_, response) => {
assert.deepEqual(response, mockResponse);
@ -112,7 +120,9 @@ describe("ThrottlingRetryPolicy", () => {
it("should return sleep interval value in milliseconds for full date format", function (done) {
const clock = sinon.useFakeTimers(new Date("Fri, 31 Dec 1999 23:00:00 GMT").getTime());
const retryAfter = ThrottlingRetryPolicy.parseRetryAfterHeader("Fri, 31 Dec 1999 23:02:00 GMT");
const retryAfter = ThrottlingRetryPolicy.parseRetryAfterHeader(
"Fri, 31 Dec 1999 23:02:00 GMT"
);
assert.equal(retryAfter, 2 * 60 * 1000);

Просмотреть файл

@ -12,11 +12,11 @@ import { createProxyAgent, createTunnel } from "../lib/proxyAgent";
describe("proxyAgent", () => {
describe("createProxyAgent", () => {
type HttpsAgent = https.Agent & {
defaultPort: number | undefined,
defaultPort: number | undefined;
options: {
proxy: tunnel.ProxyOptions
},
proxyOptions: tunnel.ProxyOptions
proxy: tunnel.ProxyOptions;
};
proxyOptions: tunnel.ProxyOptions;
};
[
@ -24,13 +24,15 @@ describe("proxyAgent", () => {
{ proxy: "http", request: "http", port: undefined, isProxyHttps: false },
{ proxy: "hTtp", request: "https", port: 443, isProxyHttps: true },
{ proxy: "HTTPS", request: "http", port: undefined, isProxyHttps: false },
{ proxy: "https", request: "hTTps", port: 443, isProxyHttps: true }
].forEach(testCase => {
it(`should return ${testCase.isProxyHttps ? "HTTPS" : "HTTP"} proxy for ${testCase.proxy.toUpperCase()} proxy server and ${testCase.request.toUpperCase()} request`, function (done) {
{ proxy: "https", request: "hTTps", port: 443, isProxyHttps: true },
].forEach((testCase) => {
it(`should return ${
testCase.isProxyHttps ? "HTTPS" : "HTTP"
} proxy for ${testCase.proxy.toUpperCase()} proxy server and ${testCase.request.toUpperCase()} request`, function (done) {
const urlHost = "proxy.microsoft.com";
const proxySettings = {
host: `${testCase.proxy}://${urlHost}`,
port: 8080
port: 8080,
};
const requestUrl = `${testCase.request}://example.com`;
@ -48,10 +50,10 @@ describe("proxyAgent", () => {
it("should copy headers correctly", function (done) {
const proxySettings = {
host: "http://proxy.microsoft.com",
port: 8080
port: 8080,
};
const headers = new HttpHeaders({
"User-Agent": "Node.js"
"User-Agent": "Node.js",
});
const proxyAgent = createProxyAgent("http://example.com", proxySettings, headers);
@ -65,24 +67,24 @@ describe("proxyAgent", () => {
describe("createTunnel", () => {
const defaultProxySettings = {
host: "http://proxy.microsoft.com",
port: 8080
port: 8080,
};
type HttpsAgent = https.Agent & {
defaultPort: number | undefined,
defaultPort: number | undefined;
options: {
proxy: tunnel.ProxyOptions
}
proxy: tunnel.ProxyOptions;
};
};
[true, false].forEach(value => {
[true, false].forEach((value) => {
it(`returns HTTP agent for HTTP request and HTTP${value ? "S" : ""} proxy`, function () {
const tunnelConfig: tunnel.HttpsOverHttpsOptions = {
proxy: {
host: defaultProxySettings.host,
port: defaultProxySettings.port,
headers: {}
}
headers: {},
},
};
const tunnel = createTunnel(false, value, tunnelConfig) as HttpsAgent;
@ -92,14 +94,14 @@ describe("proxyAgent", () => {
});
});
[true, false].forEach(value => {
[true, false].forEach((value) => {
it(`returns HTTPS agent for HTTPS request and HTTP${value ? "S" : ""} proxy`, function () {
const tunnelConfig: tunnel.HttpsOverHttpsOptions = {
proxy: {
host: defaultProxySettings.host,
port: defaultProxySettings.port,
headers: {}
}
headers: {},
},
};
const tunnel = createTunnel(true, value, tunnelConfig) as HttpsAgent;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -109,23 +109,32 @@ describe("URLQuery", () => {
});
it(`parse with base64 token as last parameter`, () => {
const expectedValue = "ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse("?api-version=2020-06-01&bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==");
const expectedValue =
"ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse(
"?api-version=2020-06-01&bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz=="
);
assert.strictEqual(parsedURL.get("bas64Value"), expectedValue);
assert.strictEqual(parsedURL.get("api-version"), "2020-06-01");
});
it(`parse with base64 token as middle parameter`, () => {
const expectedValue = "ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse("?api-version=2020-06-01&bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==&extraParam=foo");
const expectedValue =
"ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse(
"?api-version=2020-06-01&bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==&extraParam=foo"
);
assert.strictEqual(parsedURL.get("bas64Value"), expectedValue);
assert.strictEqual(parsedURL.get("api-version"), "2020-06-01");
assert.strictEqual(parsedURL.get("extraParam"), "foo");
});
it(`parse with base64 token as first parameter`, () => {
const expectedValue = "ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse("?bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==&api-version=2020-06-01");
const expectedValue =
"ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==";
const parsedURL = URLQuery.parse(
"?bas64Value=ODIzZnNkdi01YXNjLTEzc2EtMTI1NS1jYTNhc2QxMmRhMyEvU3Vic2NyaXB0aW9ucy8xMTExMTEtMTExMS0xMTExLTExMTEtMTExMTExMTExL1Jlc291cmNlR3JvdXBzL0FFLVdPUktFUlMvVk1TY2FsZVNldHMvQUUtV09SS0VSUy1TQ0FMRS0xMDAwL1ZNcy8xNDMz==&api-version=2020-06-01"
);
assert.strictEqual(parsedURL.get("bas64Value"), expectedValue);
assert.strictEqual(parsedURL.get("api-version"), "2020-06-01");
});
@ -283,7 +292,10 @@ describe("URLBuilder", () => {
assert.strictEqual(urlBuilder.getScheme(), "https");
assert.strictEqual(urlBuilder.getHost(), "www.example.com");
assert.strictEqual(urlBuilder.getPath(), "mypath");
assert.strictEqual(urlBuilder.toString(), "https://www.example.com/mypath?thing=stuff&otherthing=otherstuff");
assert.strictEqual(
urlBuilder.toString(),
"https://www.example.com/mypath?thing=stuff&otherthing=otherstuff"
);
});
it(`to "https" and setHost() to "www.example.com" and setPath() to "http://www.othersite.com/mypath?thing=stuff" and setQueryParameter() to "otherthing=otherstuff"`, () => {
@ -294,7 +306,10 @@ describe("URLBuilder", () => {
urlBuilder.setQueryParameter("otherthing", "otherstuff");
assert.strictEqual(urlBuilder.getScheme(), "http");
assert.strictEqual(urlBuilder.getPath(), "/mypath");
assert.strictEqual(urlBuilder.toString(), "http://www.othersite.com/mypath?thing=stuff&otherthing=otherstuff");
assert.strictEqual(
urlBuilder.toString(),
"http://www.othersite.com/mypath?thing=stuff&otherthing=otherstuff"
);
});
});
@ -831,23 +846,38 @@ describe("URLBuilder", () => {
});
it(`with "ftp://www.bing.com:8080"`, () => {
assert.strictEqual(URLBuilder.parse("ftp://www.bing.com:8080").toString(), "ftp://www.bing.com:8080");
assert.strictEqual(
URLBuilder.parse("ftp://www.bing.com:8080").toString(),
"ftp://www.bing.com:8080"
);
});
it(`with "www.bing.com/my/path"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com/my/path").toString(), "www.bing.com/my/path");
assert.strictEqual(
URLBuilder.parse("www.bing.com/my/path").toString(),
"www.bing.com/my/path"
);
});
it(`with "ftp://www.bing.com/my/path"`, () => {
assert.strictEqual(URLBuilder.parse("ftp://www.bing.com/my/path").toString(), "ftp://www.bing.com/my/path");
assert.strictEqual(
URLBuilder.parse("ftp://www.bing.com/my/path").toString(),
"ftp://www.bing.com/my/path"
);
});
it(`with "www.bing.com:1234/my/path"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com:1234/my/path").toString(), "www.bing.com:1234/my/path");
assert.strictEqual(
URLBuilder.parse("www.bing.com:1234/my/path").toString(),
"www.bing.com:1234/my/path"
);
});
it(`with "ftp://www.bing.com:1234/my/path"`, () => {
assert.strictEqual(URLBuilder.parse("ftp://www.bing.com:1234/my/path").toString(), "ftp://www.bing.com:1234/my/path");
assert.strictEqual(
URLBuilder.parse("ftp://www.bing.com:1234/my/path").toString(),
"ftp://www.bing.com:1234/my/path"
);
});
it(`with "www.bing.com?a=1"`, () => {
@ -855,67 +885,115 @@ describe("URLBuilder", () => {
});
it(`with "https://www.bing.com?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com?a=1").toString(), "https://www.bing.com?a=1");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com?a=1").toString(),
"https://www.bing.com?a=1"
);
});
it(`with "www.bing.com:123?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com:123?a=1").toString(), "www.bing.com:123?a=1");
assert.strictEqual(
URLBuilder.parse("www.bing.com:123?a=1").toString(),
"www.bing.com:123?a=1"
);
});
it(`with "https://www.bing.com:987?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com:987?a=1").toString(), "https://www.bing.com:987?a=1");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com:987?a=1").toString(),
"https://www.bing.com:987?a=1"
);
});
it(`with "www.bing.com/folder/index.html?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com/folder/index.html?a=1").toString(), "www.bing.com/folder/index.html?a=1");
assert.strictEqual(
URLBuilder.parse("www.bing.com/folder/index.html?a=1").toString(),
"www.bing.com/folder/index.html?a=1"
);
});
it(`with "https://www.bing.com/image.gif?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com/image.gif?a=1").toString(), "https://www.bing.com/image.gif?a=1");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com/image.gif?a=1").toString(),
"https://www.bing.com/image.gif?a=1"
);
});
it(`with "www.bing.com:123/index.html?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com:123/index.html?a=1").toString(), "www.bing.com:123/index.html?a=1");
assert.strictEqual(
URLBuilder.parse("www.bing.com:123/index.html?a=1").toString(),
"www.bing.com:123/index.html?a=1"
);
});
it(`with "https://www.bing.com:987/my/path/again?a=1"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com:987/my/path/again?a=1").toString(), "https://www.bing.com:987/my/path/again?a=1");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com:987/my/path/again?a=1").toString(),
"https://www.bing.com:987/my/path/again?a=1"
);
});
it(`with "www.bing.com?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com?a=1&b=2").toString(), "www.bing.com?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("www.bing.com?a=1&b=2").toString(),
"www.bing.com?a=1&b=2"
);
});
it(`with "https://www.bing.com?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com?a=1&b=2").toString(), "https://www.bing.com?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com?a=1&b=2").toString(),
"https://www.bing.com?a=1&b=2"
);
});
it(`with "www.bing.com:123?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com:123?a=1&b=2").toString(), "www.bing.com:123?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("www.bing.com:123?a=1&b=2").toString(),
"www.bing.com:123?a=1&b=2"
);
});
it(`with "https://www.bing.com:987?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com:987?a=1&b=2").toString(), "https://www.bing.com:987?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com:987?a=1&b=2").toString(),
"https://www.bing.com:987?a=1&b=2"
);
});
it(`with "www.bing.com/folder/index.html?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com/folder/index.html?a=1&b=2").toString(), "www.bing.com/folder/index.html?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("www.bing.com/folder/index.html?a=1&b=2").toString(),
"www.bing.com/folder/index.html?a=1&b=2"
);
});
it(`with "https://www.bing.com/image.gif?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com/image.gif?a=1&b=2").toString(), "https://www.bing.com/image.gif?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com/image.gif?a=1&b=2").toString(),
"https://www.bing.com/image.gif?a=1&b=2"
);
});
it(`with "www.bing.com:123/index.html?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("www.bing.com:123/index.html?a=1&b=2").toString(), "www.bing.com:123/index.html?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("www.bing.com:123/index.html?a=1&b=2").toString(),
"www.bing.com:123/index.html?a=1&b=2"
);
});
it(`with "https://www.bing.com:987/my/path/again?a=1&b=2"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com:987/my/path/again?a=1&b=2").toString(), "https://www.bing.com:987/my/path/again?a=1&b=2");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com:987/my/path/again?a=1&b=2").toString(),
"https://www.bing.com:987/my/path/again?a=1&b=2"
);
});
it(`with "https://www.bing.com/my:/path"`, () => {
assert.strictEqual(URLBuilder.parse("https://www.bing.com/my:/path").toString(), "https://www.bing.com/my:/path");
assert.strictEqual(
URLBuilder.parse("https://www.bing.com/my:/path").toString(),
"https://www.bing.com/my:/path"
);
});
});
@ -1005,12 +1083,30 @@ describe("URLTokenizer", () => {
const tokenizer = new URLTokenizer(text);
if (expectedURLTokens) {
for (let i = 0; i < expectedURLTokens.length; ++i) {
assert.strictEqual(tokenizer.next(), true, `Expected to find ${expectedURLTokens.length} URLTokens, but found ${i} instead.`);
assert.deepEqual(tokenizer.current(), expectedURLTokens[i], `Expected the ${i + 1} URLToken to be ${JSON.stringify(expectedURLTokens[i])}, but found ${JSON.stringify(tokenizer.current())} instead.`);
assert.strictEqual(
tokenizer.next(),
true,
`Expected to find ${expectedURLTokens.length} URLTokens, but found ${i} instead.`
);
assert.deepEqual(
tokenizer.current(),
expectedURLTokens[i],
`Expected the ${i + 1} URLToken to be ${JSON.stringify(
expectedURLTokens[i]
)}, but found ${JSON.stringify(tokenizer.current())} instead.`
);
}
}
assert.strictEqual(tokenizer.next(), false, `Only expected to find ${(expectedURLTokens ? expectedURLTokens.length : 0)} URL token(s).`);
assert.strictEqual(tokenizer.current(), undefined, `After reading all of the URLTokens, expected the current value to be undefined.`);
assert.strictEqual(
tokenizer.next(),
false,
`Only expected to find ${expectedURLTokens ? expectedURLTokens.length : 0} URL token(s).`
);
assert.strictEqual(
tokenizer.current(),
undefined,
`After reading all of the URLTokens, expected the current value to be undefined.`
);
}
it(`with ""`, () => {
@ -1018,37 +1114,25 @@ describe("URLTokenizer", () => {
});
it(`with "http"`, () => {
nextTest("http", [
URLToken.host("http")
]);
nextTest("http", [URLToken.host("http")]);
});
it(`with "http:"`, () => {
nextTest("http:", [
URLToken.host("http"),
URLToken.port("")
]);
nextTest("http:", [URLToken.host("http"), URLToken.port("")]);
});
it(`with "http:/"`, () => {
nextTest("http:/", [
URLToken.host("http"),
URLToken.port(""),
URLToken.path("/")
]);
nextTest("http:/", [URLToken.host("http"), URLToken.port(""), URLToken.path("/")]);
});
it(`with "http://"`, () => {
nextTest("http://", [
URLToken.scheme("http"),
URLToken.host("")
]);
nextTest("http://", [URLToken.scheme("http"), URLToken.host("")]);
});
it(`with "https://www.example.com"`, () => {
nextTest("https://www.example.com", [
URLToken.scheme("https"),
URLToken.host("www.example.com")
URLToken.host("www.example.com"),
]);
});
@ -1056,7 +1140,7 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com:", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.port("")
URLToken.port(""),
]);
});
@ -1064,7 +1148,7 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com:8080", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.port("8080")
URLToken.port("8080"),
]);
});
@ -1073,7 +1157,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("ftp"),
URLToken.host("www.bing.com"),
URLToken.port("123"),
URLToken.path("/")
URLToken.path("/"),
]);
});
@ -1082,7 +1166,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("ftp"),
URLToken.host("www.bing.com"),
URLToken.port("123"),
URLToken.path("/a/b/c.txt")
URLToken.path("/a/b/c.txt"),
]);
});
@ -1091,7 +1175,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("ftp"),
URLToken.host("www.bing.com"),
URLToken.port("123"),
URLToken.query("")
URLToken.query(""),
]);
});
@ -1100,7 +1184,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("ftp"),
URLToken.host("www.bing.com"),
URLToken.port("123"),
URLToken.query("a=b&c=d")
URLToken.query("a=b&c=d"),
]);
});
@ -1108,7 +1192,7 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com/", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.path("/")
URLToken.path("/"),
]);
});
@ -1116,7 +1200,7 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com/index.html", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.path("/index.html")
URLToken.path("/index.html"),
]);
});
@ -1125,7 +1209,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.path("/index.html"),
URLToken.query("")
URLToken.query(""),
]);
});
@ -1134,7 +1218,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.path("/index.html"),
URLToken.query("")
URLToken.query(""),
]);
});
@ -1143,7 +1227,7 @@ describe("URLTokenizer", () => {
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.path("/index.html"),
URLToken.query("alpha=beta")
URLToken.query("alpha=beta"),
]);
});
@ -1151,7 +1235,7 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com?", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.query("")
URLToken.query(""),
]);
});
@ -1159,36 +1243,24 @@ describe("URLTokenizer", () => {
nextTest("https://www.example.com?a=b", [
URLToken.scheme("https"),
URLToken.host("www.example.com"),
URLToken.query("a=b")
URLToken.query("a=b"),
]);
});
it(`with "www.test.com/"`, () => {
nextTest("www.test.com/", [
URLToken.host("www.test.com"),
URLToken.path("/")
]);
nextTest("www.test.com/", [URLToken.host("www.test.com"), URLToken.path("/")]);
});
it(`with "www.test.com?"`, () => {
nextTest("www.test.com?", [
URLToken.host("www.test.com"),
URLToken.query("")
]);
nextTest("www.test.com?", [URLToken.host("www.test.com"), URLToken.query("")]);
});
it(`with "folder/index.html"`, () => {
nextTest("folder/index.html", [
URLToken.host("folder"),
URLToken.path("/index.html")
]);
nextTest("folder/index.html", [URLToken.host("folder"), URLToken.path("/index.html")]);
});
it(`with "/folder/index.html"`, () => {
nextTest("/folder/index.html", [
URLToken.host(""),
URLToken.path("/folder/index.html")
]);
nextTest("/folder/index.html", [URLToken.host(""), URLToken.path("/folder/index.html")]);
});
});
});

Просмотреть файл

@ -6,45 +6,47 @@ import { WebResource } from "../lib/webResource";
import { assert } from "chai";
import { parseHeaders, XhrHttpClient } from "../lib/xhrHttpClient";
describe("XhrHttpClient", function() {
it("parses headers", function() {
describe("XhrHttpClient", function () {
it("parses headers", function () {
const xhr = {
getAllResponseHeaders: () =>
"Content-Length: 42\r\n" +
"value: hello\r\n"
getAllResponseHeaders: () => "Content-Length: 42\r\n" + "value: hello\r\n",
} as XMLHttpRequest;
const headers = parseHeaders(xhr);
assert.strictEqual(headers.get("content-length"), "42");
assert.strictEqual(headers.get("value"), "hello");
});
it("parses empty string headers", function() {
it("parses empty string headers", function () {
const xhr = {
getAllResponseHeaders: () =>
"Content-Type: \r\n" + // preserve trailing whitespace in test case
"value:\r\n"
"value:\r\n",
} as XMLHttpRequest;
const headers = parseHeaders(xhr);
assert.strictEqual(headers.get("content-type"), "");
assert.strictEqual(headers.get("value"), "");
});
it("throws when proxy settings are passed", function() {
it("throws when proxy settings are passed", function () {
const request = new WebResource();
request.proxySettings = {
host: "1.1.1.1",
port: 8080
port: 8080,
};
const client = new XhrHttpClient();
assert.throws(() => { client.sendRequest(request); }, Error);
assert.throws(() => {
client.sendRequest(request);
}, Error);
});
it("throws when agent settings are passed", function() {
it("throws when agent settings are passed", function () {
const request = new WebResource();
request.agentSettings = {} as AgentSettings;
const client = new XhrHttpClient();
assert.throws(() => { client.sendRequest(request); }, Error);
assert.throws(() => {
client.sendRequest(request);
}, Error);
});
});

Просмотреть файл

@ -6,13 +6,21 @@ describe("XML serializer", function () {
describe("parseXML(string)", function () {
it("with undefined", async function () {
const error: Error = await msAssert.throwsAsync(parseXML(undefined as any));
assert.notStrictEqual(error.message.indexOf("Document is empty"), -1, `error.message ("${error.message}") should have contained "Document is empty"`);
assert.notStrictEqual(
error.message.indexOf("Document is empty"),
-1,
`error.message ("${error.message}") should have contained "Document is empty"`
);
});
it("with null", async function () {
// tslint:disable-next-line:no-null-keyword
const error: Error = await msAssert.throwsAsync(parseXML(null as any));
assert.notStrictEqual(error.message.indexOf("Document is empty"), -1, `error.message ("${error.message}") should have contained "Document is empty"`);
assert.notStrictEqual(
error.message.indexOf("Document is empty"),
-1,
`error.message ("${error.message}") should have contained "Document is empty"`
);
});
it("with empty", async function () {
@ -31,9 +39,9 @@ describe("XML serializer", function () {
it("with empty element with attribute", async function () {
const xml: any = await parseXML(`<fruit healthy="true" />`);
assert.deepStrictEqual(xml, {
"$": {
"healthy": "true"
}
$: {
healthy: "true",
},
});
});
@ -50,56 +58,56 @@ describe("XML serializer", function () {
it("with element with attribute", async function () {
const xml: any = await parseXML(`<fruit healthy="true"></fruit>`);
assert.deepStrictEqual(xml, {
"$": {
"healthy": "true"
}
$: {
healthy: "true",
},
});
});
it("with element with attribute and value", async function () {
const xml: any = await parseXML(`<fruit healthy="true">yum</fruit>`);
assert.deepStrictEqual(xml, {
"$": {
"healthy": "true"
$: {
healthy: "true",
},
"_": "yum"
_: "yum",
});
});
it("with element with child empty element", async function () {
const xml: any = await parseXML(`<fruit><apples/></fruit>`);
assert.deepStrictEqual(xml, {
"apples": ``
apples: ``,
});
});
it("with element with child empty element with attribute", async function () {
const xml: any = await parseXML(`<fruit><apples tasty="true"/></fruit>`);
assert.deepStrictEqual(xml, {
"apples": {
"$": {
"tasty": "true"
}
}
apples: {
$: {
tasty: "true",
},
},
});
});
it("with element with child element with value", async function () {
const xml: any = await parseXML(`<fruit><apples>yum</apples></fruit>`);
assert.deepStrictEqual(xml, {
"apples": "yum"
apples: "yum",
});
});
it("with element with child element with attribute and value", async function () {
const xml: any = await parseXML(`<fruit><apples tasty="true">yum</apples></fruit>`);
assert.deepStrictEqual(xml, {
"apples": {
"$": {
"tasty": "true"
apples: {
$: {
tasty: "true",
},
"_": "yum"
}
_: "yum",
},
});
});
});

Просмотреть файл

@ -6,7 +6,13 @@
"class-name": true,
"comment-format": [true, "check-space"],
"indent": [true, "spaces"],
"ter-indent": [true, 2],
"ter-indent": [
true,
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [true, "LF"],
"one-line": [true,
"check-open-brace",

Просмотреть файл

@ -3,7 +3,7 @@ import * as glob from "glob";
import * as path from "path";
const config: webpack.Configuration = {
entry: glob.sync(path.join(__dirname, "test/**/*[^node\.].ts")),
entry: glob.sync(path.join(__dirname, "test/**/*[^node.].ts")),
mode: "development",
devtool: "source-map",
stats: {
@ -20,19 +20,37 @@ const config: webpack.Configuration = {
errors: true,
errorDetails: false,
warnings: false,
publicPath: false
publicPath: false,
},
output: {
filename: "msRest.browser.test.js",
path: path.resolve(__dirname, "test")
path: path.resolve(__dirname, "test"),
},
plugins: [
new webpack.NormalModuleReplacementPlugin(/(\.).+util\/base64/, path.resolve(__dirname, "./lib/util/base64.browser.ts")),
new webpack.NormalModuleReplacementPlugin(/(\.).+util\/xml/, path.resolve(__dirname, "./lib/util/xml.browser.ts")),
new webpack.NormalModuleReplacementPlugin(/(\.).+defaultHttpClient/, path.resolve(__dirname, "./lib/defaultHttpClient.browser.ts")),
new webpack.NormalModuleReplacementPlugin(/(\.).+msRestUserAgentPolicy/, path.resolve(__dirname, "./lib/policies/msRestUserAgentPolicy.browser.ts")),
new webpack.NormalModuleReplacementPlugin(/(\.).+agentPolicy/, path.resolve(__dirname, "./lib/policies/agentPolicy.browser.ts")),
new webpack.NormalModuleReplacementPlugin(/(\.).+proxyPolicy/, path.resolve(__dirname, "./lib/policies/proxyPolicy.browser.ts"))
new webpack.NormalModuleReplacementPlugin(
/(\.).+util\/base64/,
path.resolve(__dirname, "./lib/util/base64.browser.ts")
),
new webpack.NormalModuleReplacementPlugin(
/(\.).+util\/xml/,
path.resolve(__dirname, "./lib/util/xml.browser.ts")
),
new webpack.NormalModuleReplacementPlugin(
/(\.).+defaultHttpClient/,
path.resolve(__dirname, "./lib/defaultHttpClient.browser.ts")
),
new webpack.NormalModuleReplacementPlugin(
/(\.).+msRestUserAgentPolicy/,
path.resolve(__dirname, "./lib/policies/msRestUserAgentPolicy.browser.ts")
),
new webpack.NormalModuleReplacementPlugin(
/(\.).+agentPolicy/,
path.resolve(__dirname, "./lib/policies/agentPolicy.browser.ts")
),
new webpack.NormalModuleReplacementPlugin(
/(\.).+proxyPolicy/,
path.resolve(__dirname, "./lib/policies/proxyPolicy.browser.ts")
),
],
module: {
rules: [
@ -40,12 +58,12 @@ const config: webpack.Configuration = {
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /(node_modules)/,
options: { configFile: path.join(__dirname, "./tsconfig.es.json") }
}
]
options: { configFile: path.join(__dirname, "./tsconfig.es.json") },
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"]
extensions: [".tsx", ".ts", ".js"],
},
node: {
dns: false,
@ -57,7 +75,7 @@ const config: webpack.Configuration = {
tty: false,
tunnel: "empty",
v8: false,
}
},
};
export = config;