@azure/core-amqp
Advanced tools
Comparing version 1.0.0-dev.20200106.1 to 1.0.0-dev.20200107.1
@@ -7,2 +7,8 @@ # Release History | ||
- Added logging around the network connectivity check. | ||
- Updated the translate() utility function used to convert AmqpError or system errors to MessagingError as below: | ||
- Non-messaging errors like TypeError, RangeError or any Node.js system errors not related to network issues | ||
are returned as is instead of being converted to a MessagingError. | ||
- If a MessagingError is returned by translate(), use code instead of the name property to | ||
differentiate between different kinds of messaging errors. | ||
The name property henceforth will always be "MessagingError" on this error class. | ||
@@ -9,0 +15,0 @@ ## 1.0.0-preview.6 (2019-12-03) |
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
import { isAmqpError } from "rhea-promise"; | ||
import { isNode } from "../src/util/utils"; | ||
import { isAmqpError as rheaIsAmqpError } from "rhea-promise"; | ||
import { isNode, isString, isNumber } from "../src/util/utils"; | ||
/** | ||
@@ -436,2 +436,11 @@ * Maps the conditions to the numeric AMQP Response status codes. | ||
})(ErrorNameConditionMapper || (ErrorNameConditionMapper = {})); | ||
const systemErrorFieldsToCopy = [ | ||
"address", | ||
"code", | ||
"errno", | ||
"info", | ||
"port", | ||
"stack", | ||
"syscall" | ||
]; | ||
/** | ||
@@ -445,4 +454,6 @@ * Describes the base class for Messaging Error. | ||
* @param {string} message The error message that provides more information about the error. | ||
* @param originalError An error whose properties will be copied to the MessagingError if the | ||
* property matches one found on the Node.js `SystemError`. | ||
*/ | ||
constructor(message) { | ||
constructor(message, originalError) { | ||
super(message); | ||
@@ -454,6 +465,2 @@ /** | ||
/** | ||
* @property {boolean} translated Has the error been translated. Default: true. | ||
*/ | ||
this.translated = true; | ||
/** | ||
* | ||
@@ -463,2 +470,11 @@ * @property {boolean} retryable Describes whether the error is retryable. Default: true. | ||
this.retryable = true; | ||
if (!originalError) { | ||
return; | ||
} | ||
// copy properties from system error | ||
for (const propName of systemErrorFieldsToCopy) { | ||
if (originalError[propName] != undefined) { | ||
this[propName] = originalError[propName]; | ||
} | ||
} | ||
} | ||
@@ -506,11 +522,17 @@ } | ||
})(SystemErrorConditionMapper || (SystemErrorConditionMapper = {})); | ||
/** | ||
* Checks whether the provided error is a node.js SystemError. | ||
* @param err An object that may contain error information. | ||
*/ | ||
export function isSystemError(err) { | ||
let result = false; | ||
if (err.code && | ||
typeof err.code === "string" && | ||
(err.syscall && typeof err.syscall === "string") && | ||
(err.errno && (typeof err.errno === "string" || typeof err.errno === "number"))) { | ||
result = true; | ||
if (!err) { | ||
return false; | ||
} | ||
return result; | ||
if (!isString(err.code) || !isString(err.syscall)) { | ||
return false; | ||
} | ||
if (!isString(err.errno) && !isNumber(err.errno)) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
@@ -534,4 +556,13 @@ /** | ||
} | ||
const rheaPromiseErrors = [ | ||
// OperationTimeoutError occurs when the service fails to respond within a given timeframe. | ||
"OperationTimeoutError", | ||
// InsufficientCreditError occurs when the number of credits available on Rhea link is insufficient. | ||
"InsufficientCreditError", | ||
// Defines the error that occurs when the Sender fails to send a message. | ||
"SendOperationFailedError" | ||
]; | ||
/** | ||
* Translates the AQMP error received at the protocol layer or a generic Error into a MessagingError. | ||
* Translates the AQMP error received at the protocol layer or a SystemError into a MessagingError. | ||
* All other errors are returned unaltered. | ||
* | ||
@@ -542,12 +573,6 @@ * @param {AmqpError} err The amqp error that was received. | ||
export function translate(err) { | ||
if (err.translated) { | ||
// already translated | ||
return err; | ||
} | ||
let error = err; | ||
// Built-in errors like TypeError and RangeError should not be retryable as these indicate issues | ||
// with user input and not an issue with the Messaging process. | ||
if (err instanceof TypeError || err instanceof RangeError) { | ||
error.retryable = false; | ||
return error; | ||
return err; | ||
} | ||
@@ -558,18 +583,15 @@ if (isAmqpError(err)) { | ||
const description = err.description; | ||
error = new MessagingError(description); | ||
const error = new MessagingError(description); | ||
if (err.stack) | ||
error.stack = err.stack; | ||
error.info = err.info; | ||
error.condition = condition; | ||
if (condition) { | ||
error.name = ConditionErrorNameMapper[condition]; | ||
error.code = ConditionErrorNameMapper[condition]; | ||
} | ||
if (!error.name) | ||
error.name = "MessagingError"; | ||
if (description && | ||
(description.includes("status-code: 404") || | ||
description.match(/The messaging entity .* could not be found.*/i) !== null)) { | ||
error.name = "MessagingEntityNotFoundError"; | ||
error.code = "MessagingEntityNotFoundError"; | ||
} | ||
if (retryableErrors.indexOf(error.name) === -1) { | ||
if (error.code && retryableErrors.indexOf(error.code) === -1) { | ||
// not found | ||
@@ -580,2 +602,6 @@ error.retryable = false; | ||
} | ||
if (err.name === "MessagingError") { | ||
// already translated | ||
return err; | ||
} | ||
if (isSystemError(err)) { | ||
@@ -585,12 +611,10 @@ // translate | ||
const description = err.message; | ||
error = new MessagingError(description); | ||
if (err.stack) | ||
error.stack = err.stack; | ||
const error = new MessagingError(description, err); | ||
let errorType = "SystemError"; | ||
if (condition) { | ||
const amqpErrorCondition = SystemErrorConditionMapper[condition]; | ||
error.name = ConditionErrorNameMapper[amqpErrorCondition]; | ||
errorType = | ||
ConditionErrorNameMapper[amqpErrorCondition]; | ||
} | ||
if (!error.name) | ||
error.name = "SystemError"; | ||
if (retryableErrors.indexOf(error.name) === -1) { | ||
if (retryableErrors.indexOf(errorType) === -1) { | ||
// not found | ||
@@ -603,24 +627,23 @@ error.retryable = false; | ||
// Translate browser communication errors during opening handshake to generic SeviceCommunicationError | ||
error = new MessagingError("Websocket connection failed."); | ||
error.name = ConditionErrorNameMapper[ErrorNameConditionMapper.ServiceCommunicationError]; | ||
const error = new MessagingError("Websocket connection failed."); | ||
error.code = ConditionErrorNameMapper[ErrorNameConditionMapper.ServiceCommunicationError]; | ||
error.retryable = false; | ||
return error; | ||
} | ||
// instanceof checks on custom Errors doesn't work without manually setting the prototype within the error. | ||
// Must do a name check until the custom error is updated, and that doesn't break compatibility | ||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | ||
const errorName = err.name; | ||
if (retryableErrors.indexOf(errorName) > -1) { | ||
error.retryable = true; | ||
// Some errors come from rhea-promise and need to be converted to MessagingError. | ||
// A subset of these are also retryable. | ||
if (rheaPromiseErrors.indexOf(err.name) !== -1) { | ||
const error = new MessagingError(err.message, err); | ||
error.code = err.name; | ||
if (error.code && retryableErrors.indexOf(error.code) === -1) { | ||
// not found | ||
error.retryable = false; | ||
} | ||
return error; | ||
} | ||
if (errorName === "AbortError") { | ||
error.retryable = false; | ||
return error; | ||
} | ||
// Translate a generic error into MessagingError. | ||
error = new MessagingError(err.message); | ||
error.stack = err.stack; | ||
return error; | ||
return err; | ||
} | ||
function isAmqpError(error) { | ||
return rheaIsAmqpError(error); | ||
} | ||
//# sourceMappingURL=errors.js.map |
@@ -174,2 +174,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
} | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export function isString(s) { | ||
return typeof s === "string"; | ||
} | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export function isNumber(n) { | ||
return typeof n === "number"; | ||
} | ||
//# sourceMappingURL=utils.js.map |
{ | ||
"name": "@azure/core-amqp", | ||
"sdk-type": "client", | ||
"version": "1.0.0-dev.20200106.1", | ||
"version": "1.0.0-dev.20200107.1", | ||
"description": "Common library for amqp based azure sdks like @azure/event-hubs.", | ||
@@ -6,0 +6,0 @@ "author": "Microsoft Corporation", |
@@ -233,3 +233,3 @@ # Azure Core AMQP client library for AMQP operations | ||
If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/tree/beb20d6071254ba0e5b5517c758acd7d38abe0cd/CONTRIBUTING.md) to learn more about how to build and test the code. | ||
If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/tree/1278511d2aecc4567cf9214e0fac90d3f7e9a03e/CONTRIBUTING.md) to learn more about how to build and test the code. | ||
@@ -236,0 +236,0 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). |
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
import { AmqpResponseStatusCode, isAmqpError, AmqpError } from "rhea-promise"; | ||
import { isNode } from "../src/util/utils"; | ||
import { AmqpResponseStatusCode, isAmqpError as rheaIsAmqpError, AmqpError } from "rhea-promise"; | ||
import { isNode, isString, isNumber } from "../src/util/utils"; | ||
@@ -439,2 +439,29 @@ /** | ||
/** | ||
* Describes the fields on a Node.js SystemError. | ||
* Omits fields that are not related to network calls (e.g. file system calls). | ||
* See https://nodejs.org/dist/latest-v12.x/docs/api/errors.html#errors_class_systemerror | ||
*/ | ||
interface NetworkSystemError { | ||
address?: string; | ||
code: string; | ||
errno: string | number; | ||
info?: any; | ||
message: string; | ||
name: string; | ||
port?: number; | ||
stack: string; | ||
syscall: string; | ||
} | ||
const systemErrorFieldsToCopy: (keyof Omit<NetworkSystemError, "name" | "message">)[] = [ | ||
"address", | ||
"code", | ||
"errno", | ||
"info", | ||
"port", | ||
"stack", | ||
"syscall" | ||
]; | ||
/** | ||
* Describes the base class for Messaging Error. | ||
@@ -446,6 +473,16 @@ * @class {MessagingError} | ||
/** | ||
* @property {string} [condition] The error condition. | ||
* Address to which the network connection failed. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
condition?: string; | ||
address?: string; | ||
/** | ||
* A string label that identifies the error. | ||
*/ | ||
code?: string; | ||
/** | ||
* System-provided error number. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
errno?: number | string; | ||
/** | ||
* @property {string} name The error name. Default value: "MessagingError". | ||
@@ -455,6 +492,12 @@ */ | ||
/** | ||
* @property {boolean} translated Has the error been translated. Default: true. | ||
* The unavailable network connection port. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
translated: boolean = true; | ||
port?: number; | ||
/** | ||
* Name of the system call that triggered the error. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
syscall?: string; | ||
/** | ||
* | ||
@@ -465,3 +508,3 @@ * @property {boolean} retryable Describes whether the error is retryable. Default: true. | ||
/** | ||
* @property {any} [info] Any additional error information given by the service. | ||
* @property {any} [info] Extra details about the error. | ||
*/ | ||
@@ -471,5 +514,18 @@ info?: any; | ||
* @param {string} message The error message that provides more information about the error. | ||
* @param originalError An error whose properties will be copied to the MessagingError if the | ||
* property matches one found on the Node.js `SystemError`. | ||
*/ | ||
constructor(message: string) { | ||
constructor(message: string, originalError?: Error) { | ||
super(message); | ||
if (!originalError) { | ||
return; | ||
} | ||
// copy properties from system error | ||
for (const propName of systemErrorFieldsToCopy) { | ||
if ((originalError as NetworkSystemError)[propName] != undefined) { | ||
this[propName] = (originalError as NetworkSystemError)[propName]; | ||
} | ||
} | ||
} | ||
@@ -522,13 +578,20 @@ } | ||
export function isSystemError(err: any): boolean { | ||
let result: boolean = false; | ||
if ( | ||
err.code && | ||
typeof err.code === "string" && | ||
(err.syscall && typeof err.syscall === "string") && | ||
(err.errno && (typeof err.errno === "string" || typeof err.errno === "number")) | ||
) { | ||
result = true; | ||
/** | ||
* Checks whether the provided error is a node.js SystemError. | ||
* @param err An object that may contain error information. | ||
*/ | ||
export function isSystemError(err: any): err is NetworkSystemError { | ||
if (!err) { | ||
return false; | ||
} | ||
return result; | ||
if (!isString(err.code) || !isString(err.syscall)) { | ||
return false; | ||
} | ||
if (!isString(err.errno) && !isNumber(err.errno)) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
@@ -556,4 +619,16 @@ | ||
const rheaPromiseErrors = [ | ||
// OperationTimeoutError occurs when the service fails to respond within a given timeframe. | ||
"OperationTimeoutError", | ||
// InsufficientCreditError occurs when the number of credits available on Rhea link is insufficient. | ||
"InsufficientCreditError", | ||
// Defines the error that occurs when the Sender fails to send a message. | ||
"SendOperationFailedError" | ||
]; | ||
/** | ||
* Translates the AQMP error received at the protocol layer or a generic Error into a MessagingError. | ||
* Translates the AQMP error received at the protocol layer or a SystemError into a MessagingError. | ||
* All other errors are returned unaltered. | ||
* | ||
@@ -563,15 +638,7 @@ * @param {AmqpError} err The amqp error that was received. | ||
*/ | ||
export function translate(err: AmqpError | Error): MessagingError { | ||
if ((err as MessagingError).translated) { | ||
// already translated | ||
return err as MessagingError; | ||
} | ||
let error: MessagingError = err as MessagingError; | ||
export function translate(err: AmqpError | Error): MessagingError | Error { | ||
// Built-in errors like TypeError and RangeError should not be retryable as these indicate issues | ||
// with user input and not an issue with the Messaging process. | ||
if (err instanceof TypeError || err instanceof RangeError) { | ||
error.retryable = false; | ||
return error; | ||
return err; | ||
} | ||
@@ -581,12 +648,10 @@ | ||
// translate | ||
const condition = (err as AmqpError).condition; | ||
const description = (err as AmqpError).description as string; | ||
error = new MessagingError(description); | ||
const condition = err.condition; | ||
const description = err.description!; | ||
const error = new MessagingError(description); | ||
if ((err as any).stack) error.stack = (err as any).stack; | ||
error.info = (err as AmqpError).info; | ||
error.condition = condition; | ||
error.info = err.info; | ||
if (condition) { | ||
error.name = ConditionErrorNameMapper[condition as keyof typeof ConditionErrorNameMapper]; | ||
error.code = ConditionErrorNameMapper[condition as keyof typeof ConditionErrorNameMapper]; | ||
} | ||
if (!error.name) error.name = "MessagingError"; | ||
if ( | ||
@@ -597,5 +662,5 @@ description && | ||
) { | ||
error.name = "MessagingEntityNotFoundError"; | ||
error.code = "MessagingEntityNotFoundError"; | ||
} | ||
if (retryableErrors.indexOf(error.name) === -1) { | ||
if (error.code && retryableErrors.indexOf(error.code) === -1) { | ||
// not found | ||
@@ -607,14 +672,20 @@ error.retryable = false; | ||
if (err.name === "MessagingError") { | ||
// already translated | ||
return err; | ||
} | ||
if (isSystemError(err)) { | ||
// translate | ||
const condition = (err as any).code; | ||
const description = (err as Error).message; | ||
error = new MessagingError(description); | ||
if ((err as any).stack) error.stack = (err as any).stack; | ||
const condition = err.code; | ||
const description = err.message; | ||
const error = new MessagingError(description, err); | ||
let errorType = "SystemError"; | ||
if (condition) { | ||
const amqpErrorCondition = SystemErrorConditionMapper[condition as keyof typeof SystemErrorConditionMapper]; | ||
error.name = ConditionErrorNameMapper[amqpErrorCondition as keyof typeof ConditionErrorNameMapper]; | ||
const amqpErrorCondition = | ||
SystemErrorConditionMapper[condition as keyof typeof SystemErrorConditionMapper]; | ||
errorType = | ||
ConditionErrorNameMapper[amqpErrorCondition as keyof typeof ConditionErrorNameMapper]; | ||
} | ||
if (!error.name) error.name = "SystemError"; | ||
if (retryableErrors.indexOf(error.name) === -1) { | ||
if (retryableErrors.indexOf(errorType) === -1) { | ||
// not found | ||
@@ -628,4 +699,4 @@ error.retryable = false; | ||
// Translate browser communication errors during opening handshake to generic SeviceCommunicationError | ||
error = new MessagingError("Websocket connection failed."); | ||
error.name = ConditionErrorNameMapper[ErrorNameConditionMapper.ServiceCommunicationError]; | ||
const error = new MessagingError("Websocket connection failed."); | ||
error.code = ConditionErrorNameMapper[ErrorNameConditionMapper.ServiceCommunicationError]; | ||
error.retryable = false; | ||
@@ -635,19 +706,19 @@ return error; | ||
// instanceof checks on custom Errors doesn't work without manually setting the prototype within the error. | ||
// Must do a name check until the custom error is updated, and that doesn't break compatibility | ||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | ||
const errorName = (err as Error).name; | ||
if (retryableErrors.indexOf(errorName) > -1) { | ||
error.retryable = true; | ||
// Some errors come from rhea-promise and need to be converted to MessagingError. | ||
// A subset of these are also retryable. | ||
if (rheaPromiseErrors.indexOf(err.name) !== -1) { | ||
const error = new MessagingError(err.message, err); | ||
error.code = err.name; | ||
if (error.code && retryableErrors.indexOf(error.code) === -1) { | ||
// not found | ||
error.retryable = false; | ||
} | ||
return error; | ||
} | ||
if (errorName === "AbortError") { | ||
error.retryable = false; | ||
return error; | ||
} | ||
// Translate a generic error into MessagingError. | ||
error = new MessagingError((err as Error).message); | ||
error.stack = (err as Error).stack; | ||
return error; | ||
return err; | ||
} | ||
function isAmqpError(error: any): error is AmqpError { | ||
return rheaIsAmqpError(error); | ||
} |
@@ -301,1 +301,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
} | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export function isString(s: any): s is string { | ||
return typeof s === "string"; | ||
} | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export function isNumber(n: any): n is number { | ||
return typeof n === "number"; | ||
} |
@@ -431,2 +431,18 @@ import { AmqpError } from "rhea-promise"; | ||
/** | ||
* Describes the fields on a Node.js SystemError. | ||
* Omits fields that are not related to network calls (e.g. file system calls). | ||
* See https://nodejs.org/dist/latest-v12.x/docs/api/errors.html#errors_class_systemerror | ||
*/ | ||
interface NetworkSystemError { | ||
address?: string; | ||
code: string; | ||
errno: string | number; | ||
info?: any; | ||
message: string; | ||
name: string; | ||
port?: number; | ||
stack: string; | ||
syscall: string; | ||
} | ||
/** | ||
* Describes the base class for Messaging Error. | ||
@@ -438,6 +454,16 @@ * @class {MessagingError} | ||
/** | ||
* @property {string} [condition] The error condition. | ||
* Address to which the network connection failed. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
condition?: string; | ||
address?: string; | ||
/** | ||
* A string label that identifies the error. | ||
*/ | ||
code?: string; | ||
/** | ||
* System-provided error number. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
errno?: number | string; | ||
/** | ||
* @property {string} name The error name. Default value: "MessagingError". | ||
@@ -447,6 +473,12 @@ */ | ||
/** | ||
* @property {boolean} translated Has the error been translated. Default: true. | ||
* The unavailable network connection port. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
translated: boolean; | ||
port?: number; | ||
/** | ||
* Name of the system call that triggered the error. | ||
* Only present if the `MessagingError` was instantiated with a Node.js `SystemError`. | ||
*/ | ||
syscall?: string; | ||
/** | ||
* | ||
@@ -457,3 +489,3 @@ * @property {boolean} retryable Describes whether the error is retryable. Default: true. | ||
/** | ||
* @property {any} [info] Any additional error information given by the service. | ||
* @property {any} [info] Extra details about the error. | ||
*/ | ||
@@ -463,4 +495,6 @@ info?: any; | ||
* @param {string} message The error message that provides more information about the error. | ||
* @param originalError An error whose properties will be copied to the MessagingError if the | ||
* property matches one found on the Node.js `SystemError`. | ||
*/ | ||
constructor(message: string); | ||
constructor(message: string, originalError?: Error); | ||
} | ||
@@ -490,5 +524,10 @@ /** | ||
} | ||
export declare function isSystemError(err: any): boolean; | ||
/** | ||
* Translates the AQMP error received at the protocol layer or a generic Error into a MessagingError. | ||
* Checks whether the provided error is a node.js SystemError. | ||
* @param err An object that may contain error information. | ||
*/ | ||
export declare function isSystemError(err: any): err is NetworkSystemError; | ||
/** | ||
* Translates the AQMP error received at the protocol layer or a SystemError into a MessagingError. | ||
* All other errors are returned unaltered. | ||
* | ||
@@ -498,3 +537,4 @@ * @param {AmqpError} err The amqp error that was received. | ||
*/ | ||
export declare function translate(err: AmqpError | Error): MessagingError; | ||
export declare function translate(err: AmqpError | Error): MessagingError | Error; | ||
export {}; | ||
//# sourceMappingURL=errors.d.ts.map |
@@ -147,2 +147,12 @@ import AsyncLock from "async-lock"; | ||
export declare function isIotHubConnectionString(connectionString: string): boolean; | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export declare function isString(s: any): s is string; | ||
/** | ||
* @ignore | ||
* @internal | ||
*/ | ||
export declare function isNumber(n: any): n is number; | ||
//# sourceMappingURL=utils.d.ts.map |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2241868
22459