Moving to amqp-common and updated structure of rhea-promise
This commit is contained in:
Родитель
5d643dac3e
Коммит
df0e15445b
|
@ -33,6 +33,10 @@ node_modules
|
|||
# WebStorm
|
||||
.idea
|
||||
|
||||
# coverage #
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
# Mac OS #
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
|
@ -52,3 +56,4 @@ dist/
|
|||
output/
|
||||
client/examples/js/lib/
|
||||
processor/examples/js/lib/
|
||||
typedoc
|
|
@ -2,6 +2,7 @@ language: node_js
|
|||
|
||||
node_js:
|
||||
- "8"
|
||||
- "10"
|
||||
|
||||
before_install:
|
||||
- cd client
|
||||
|
|
|
@ -15,6 +15,7 @@ async function main(): Promise<void> {
|
|||
};
|
||||
const delivery = await client.send(data);
|
||||
console.log(">>> Sent the message successfully: ", delivery.id);
|
||||
// await (Object.values((client as any)._context.senders)[0] as any).close();
|
||||
// await client.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as crypto from "crypto";
|
||||
import { parseConnectionString, EventHubConnectionStringModel } from "../util/utils";
|
||||
import { parseConnectionString, ServiceBusConnectionStringModel } from "../util/utils";
|
||||
import { TokenInfo, TokenProvider, TokenType } from "./token";
|
||||
|
||||
/**
|
||||
|
@ -11,17 +11,17 @@ import { TokenInfo, TokenProvider, TokenType } from "./token";
|
|||
*/
|
||||
export class SasTokenProvider implements TokenProvider {
|
||||
/**
|
||||
* @property {string} namespace - The namespace of the EventHub instance.
|
||||
* @property {string} namespace - The namespace of the EventHub/ServiceBus instance.
|
||||
*/
|
||||
namespace: string;
|
||||
|
||||
/**
|
||||
* @property {string} keyName - The name of the EventHub key.
|
||||
* @property {string} keyName - The name of the EventHub/ServiceBus key.
|
||||
*/
|
||||
keyName: string;
|
||||
|
||||
/**
|
||||
* @property {string} key - The secret value associated with the above EventHub key
|
||||
* @property {string} key - The secret value associated with the above EventHub/ServiceBus key
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
|
@ -36,9 +36,9 @@ export class SasTokenProvider implements TokenProvider {
|
|||
/**
|
||||
* Initializes a new isntance of SasTokenProvider
|
||||
* @constructor
|
||||
* @param {string} namespace - The namespace of the EventHub instance.
|
||||
* @param {string} keyName - The name of the EventHub key.
|
||||
* @param {string} key - The secret value associated with the above EventHub key
|
||||
* @param {string} namespace - The namespace of the EventHub/ServiceBus instance.
|
||||
* @param {string} keyName - The name of the EventHub/ServiceBus key.
|
||||
* @param {string} key - The secret value associated with the above EventHub/ServiceBus key
|
||||
*/
|
||||
constructor(namespace: string, keyName: string, key: string, tokenValidTimeInSeconds?: number, tokenRenewalMarginInSeconds?: number) {
|
||||
this.namespace = namespace;
|
||||
|
@ -83,11 +83,11 @@ export class SasTokenProvider implements TokenProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a token provider from the EventHub connection string;
|
||||
* @param {string} connectionString - The EventHub connection string
|
||||
* Creates a token provider from the EventHub/ServiceBus connection string;
|
||||
* @param {string} connectionString - The EventHub/ServiceBus connection string
|
||||
*/
|
||||
static fromConnectionString(connectionString: string): SasTokenProvider {
|
||||
const parsed = parseConnectionString<EventHubConnectionStringModel>(connectionString);
|
||||
const parsed = parseConnectionString<ServiceBusConnectionStringModel>(connectionString);
|
||||
return new SasTokenProvider(parsed.Endpoint, parsed.SharedAccessKeyName, parsed.SharedAccessKey);
|
||||
}
|
||||
}
|
|
@ -2,21 +2,23 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { TokenInfo } from "./auth/token";
|
||||
import { RequestResponseLink, createRequestResponseLink, sendRequest } from "./rpc";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import {
|
||||
EventContext, ReceiverOptions, Message, SenderEvents, ReceiverEvents, Connection
|
||||
} from "../rhea-promise";
|
||||
import * as uuid from "uuid/v4";
|
||||
import * as Constants from "./util/constants";
|
||||
import * as debugModule from "debug";
|
||||
import * as rpc from "./rpc";
|
||||
import { Message } from ".";
|
||||
import { ConnectionConfig } from "./connectionConfig";
|
||||
import { translate } from "./errors";
|
||||
import { defaultLock } from "./util/utils";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
const debug = debugModule("azure:event-hubs:cbs");
|
||||
import { RequestResponseLink } from "./requestResponseLink";
|
||||
import { SenderOptions } from "rhea";
|
||||
const debug = debugModule("azure:amqp-common:cbs");
|
||||
|
||||
/**
|
||||
* @class CbsClient
|
||||
* Describes the EventHub Cbs client that talks to the $cbs endopint over AMQP connection.
|
||||
* Describes the EventHub/ServiceBus Cbs client that talks to the $cbs endopint over AMQP connection.
|
||||
*/
|
||||
export class CbsClient {
|
||||
/**
|
||||
|
@ -34,15 +36,42 @@ export class CbsClient {
|
|||
*/
|
||||
readonly cbsLock: string = `${Constants.negotiateCbsKey}-${uuid()}`;
|
||||
|
||||
/**
|
||||
* @property {string} connectionLock The unqiue lock name per connection that is used to
|
||||
* acquire the lock for establishing an amqp connection if one does not exist.
|
||||
*/
|
||||
readonly connectionLock: string = `${Constants.establishConnection}-${uuid()}`;
|
||||
|
||||
/**
|
||||
* @property {ConnectionConfig} config The connection config.
|
||||
*/
|
||||
config: ConnectionConfig;
|
||||
|
||||
/**
|
||||
* @property {string} pacakgeVersion The package version that will be set as a property of the
|
||||
* connection.
|
||||
*/
|
||||
packageVersion: string;
|
||||
|
||||
/**
|
||||
* @property {string} userAgent The useragent string.
|
||||
*/
|
||||
userAgent: string;
|
||||
|
||||
/**
|
||||
* @property {Connection} connection The AMQP connection.
|
||||
*/
|
||||
connection?: Connection;
|
||||
|
||||
/**
|
||||
* CBS sender, receiver on the same session.
|
||||
*/
|
||||
private _cbsSenderReceiverLink?: RequestResponseLink;
|
||||
|
||||
private _context: ConnectionContext;
|
||||
|
||||
constructor(context: ConnectionContext) {
|
||||
this._context = context;
|
||||
constructor(config: ConnectionConfig, packageVersion: string, userAgent?: string) {
|
||||
this.config = config;
|
||||
this.packageVersion = packageVersion;
|
||||
this.userAgent = userAgent || "/js-amqp-client";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,47 +82,47 @@ export class CbsClient {
|
|||
try {
|
||||
// Acquire the lock and establish an amqp connection if it does not exist.
|
||||
if (!this._isConnectionOpen()) {
|
||||
debug("[%s] The CBS client is trying to establish an AMQP connection.", this._context.connectionId);
|
||||
debug("The CBS client is trying to establish an AMQP connection.");
|
||||
const params: rpc.CreateConnectionPrameters = {
|
||||
config: this._context.config,
|
||||
userAgent: ConnectionContext.userAgent
|
||||
config: this.config,
|
||||
packageVersion: this.packageVersion,
|
||||
userAgent: this.userAgent
|
||||
};
|
||||
this._context.connection = await defaultLock.acquire(this._context.connectionLock, () => { return rpc.open(params); });
|
||||
this._context.connectionId = this._context.connection.options.id;
|
||||
this.connection = await defaultLock.acquire(this.connectionLock, () => { return rpc.open(params); });
|
||||
}
|
||||
|
||||
if (!this._cbsSenderReceiverLink) {
|
||||
const rxOpt: rheaPromise.ReceiverOptions = {
|
||||
if (!this._isCbsSenderReceiverLinkOpen()) {
|
||||
const rxOpt: ReceiverOptions = {
|
||||
source: {
|
||||
address: this.endpoint
|
||||
},
|
||||
name: this.replyTo
|
||||
};
|
||||
this._cbsSenderReceiverLink = await createRequestResponseLink(this._context.connection, { target: { address: this.endpoint } }, rxOpt);
|
||||
this._cbsSenderReceiverLink.sender.on("sender_error", (context: rheaPromise.EventContext) => {
|
||||
const srOpt: SenderOptions = { target: { address: this.endpoint } };
|
||||
this._cbsSenderReceiverLink = await RequestResponseLink.create(this.connection!, srOpt, rxOpt);
|
||||
this._cbsSenderReceiverLink.sender.registerHandler(SenderEvents.senderError, (context: EventContext) => {
|
||||
const ehError = translate(context.sender!.error!);
|
||||
debug("An error occurred on the cbs sender link.. %O", ehError);
|
||||
});
|
||||
this._cbsSenderReceiverLink.receiver.on("receiver_error", (context: rheaPromise.EventContext) => {
|
||||
this._cbsSenderReceiverLink.receiver.registerHandler(ReceiverEvents.receiverError, (context: EventContext) => {
|
||||
const ehError = translate(context.receiver!.error!);
|
||||
debug("An error occurred on the cbs receiver link.. %O", ehError);
|
||||
});
|
||||
debug("[%s] Successfully created the cbs sender '%s' and receiver '%s' links over cbs session.",
|
||||
this._context.connectionId, this._cbsSenderReceiverLink.sender.name, this._cbsSenderReceiverLink.receiver.name);
|
||||
this.connection!.id, this._cbsSenderReceiverLink.sender.name, this._cbsSenderReceiverLink.receiver.name);
|
||||
} else {
|
||||
debug("[%s] CBS session is already present. Reusing the cbs sender '%s' and receiver '%s' links over cbs session.",
|
||||
this._context.connectionId, this._cbsSenderReceiverLink.sender.name, this._cbsSenderReceiverLink.receiver.name);
|
||||
this.connection!.id, this._cbsSenderReceiverLink!.sender.name, this._cbsSenderReceiverLink!.receiver.name);
|
||||
}
|
||||
} catch (err) {
|
||||
err = translate(err);
|
||||
debug("[%s] An error occured while establishing the cbs links: %O",
|
||||
this._context.connectionId, err);
|
||||
debug("[%s] An error occured while establishing the cbs links: %O", this.connection!.id, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiates the CBS claim with the EventHub Service.
|
||||
* Negotiates the CBS claim with the EventHub/ServiceBus Service.
|
||||
* @param {string} audience The audience for which the token is requested.
|
||||
* @param {TokenInfo} tokenObject The token object that needs to be sent in the put-token request.
|
||||
* @return {Promise<any>} Returns a Promise that resolves when $cbs authentication is successful
|
||||
|
@ -112,40 +141,38 @@ export class CbsClient {
|
|||
type: tokenObject.tokenType
|
||||
}
|
||||
};
|
||||
const response = await sendRequest(this._context.connection, this._cbsSenderReceiverLink!, request);
|
||||
const response = await this._cbsSenderReceiverLink!.sendRequest(request);
|
||||
return response;
|
||||
} catch (err) {
|
||||
debug("[%s]An error occurred while negotating the cbs claim: %O", this._context.connectionId, err);
|
||||
debug("[%s] An error occurred while negotating the cbs claim: %O", this.connection!.id, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the AMQP cbs session to the Event Hub for this client,
|
||||
* Closes the AMQP cbs session to the EventHub/ServiceBus for this client,
|
||||
* returning a promise that will be resolved when disconnection is completed.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
if (this._cbsSenderReceiverLink) {
|
||||
await rheaPromise.closeSession(this._cbsSenderReceiverLink.session);
|
||||
debug("Successfully closed the cbs session.");
|
||||
if (this._isCbsSenderReceiverLinkOpen()) {
|
||||
await this._cbsSenderReceiverLink!.close();
|
||||
debug("[%s] Successfully closed the cbs session.", this.connection!.id);
|
||||
this._cbsSenderReceiverLink = undefined;
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `An error occurred while closing the cbs session: ${JSON.stringify(err)} `;
|
||||
debug(msg);
|
||||
const msg = `An error occurred while closing the cbs link: ${err.stack || JSON.stringify(err)}.`;
|
||||
debug("[%s] %s", this.connection!.id, msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private _isCbsSenderReceiverLinkOpen(): boolean {
|
||||
return this._cbsSenderReceiverLink! && this._cbsSenderReceiverLink!.isOpen();
|
||||
}
|
||||
|
||||
private _isConnectionOpen(): boolean {
|
||||
let result: boolean = false;
|
||||
if (this._context && this._context.connection) {
|
||||
if (this._context.connection.is_open && this._context.connection.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return this.connection! && this.connection!.isOpen();
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { parseConnectionString, EventHubConnectionStringModel } from "./util/utils";
|
||||
import { parseConnectionString, ServiceBusConnectionStringModel } from "./util/utils";
|
||||
|
||||
export interface ConnectionConfigOptions {
|
||||
isEntityPathRequired?: boolean;
|
||||
}
|
||||
|
||||
export interface ConnectionConfig {
|
||||
/**
|
||||
* @property {string} endpoint - The service bus endpoint "sb://<yournamespace>.servicebus.windows.net/".
|
||||
* @property {string} endpoint - The service bus endpoint
|
||||
* "sb://<yournamespace>.servicebus.windows.net/".
|
||||
*/
|
||||
endpoint: string;
|
||||
/**
|
||||
|
@ -17,7 +22,8 @@ export interface ConnectionConfig {
|
|||
*/
|
||||
connectionString: string;
|
||||
/**
|
||||
* @property {string} entityPath - The name/path of the entity (hub name) to which the connection needs to happen.
|
||||
* @property {string} entityPath - The name/path of the entity (hub name) to which the
|
||||
* connection needs to happen.
|
||||
*/
|
||||
entityPath?: string;
|
||||
/**
|
||||
|
@ -33,16 +39,19 @@ export interface ConnectionConfig {
|
|||
export namespace ConnectionConfig {
|
||||
/**
|
||||
* Creates the connection config.
|
||||
* @param {string} connectionString - The event hub connection string
|
||||
* @param {string} [path] - The name/path of the entity (hub name) to which the connection needs to happen
|
||||
* @param {string} connectionString - The connection string for a given service like
|
||||
* EventHub/ServiceBus.
|
||||
* @param {string} [path] - The name/path of the entity (hub name) to which the
|
||||
* connection needs to happen.
|
||||
*/
|
||||
export function create(connectionString: string, path?: string): ConnectionConfig {
|
||||
if (!connectionString || (connectionString && typeof connectionString !== "string")) {
|
||||
throw new Error("'connectionString' is a required parameter and must be of type: 'string'.");
|
||||
}
|
||||
const parsedCS = parseConnectionString<EventHubConnectionStringModel>(connectionString);
|
||||
const parsedCS = parseConnectionString<ServiceBusConnectionStringModel>(connectionString);
|
||||
if (!path && !parsedCS.EntityPath) {
|
||||
throw new Error(`Either provide "path" or the "connectionString": "${connectionString}", must contain EntityPath="<path-to-the-entity>".`);
|
||||
throw new Error(`Either provide "path" or the "connectionString": "${connectionString}", ` +
|
||||
`must contain EntityPath="<path-to-the-entity>".`);
|
||||
}
|
||||
const result: ConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
|
@ -59,21 +68,25 @@ export namespace ConnectionConfig {
|
|||
* Validates the properties of connection config.
|
||||
* @param {ConnectionConfig} config The connection config to be validated.
|
||||
*/
|
||||
export function validate(config: ConnectionConfig): void {
|
||||
export function validate(config: ConnectionConfig, options?: ConnectionConfigOptions): void {
|
||||
if (!options) options = {};
|
||||
if (!config || (config && typeof config !== "object")) {
|
||||
throw new Error("'config' is a required parameter and must be of type: 'object'.");
|
||||
}
|
||||
if (!config.endpoint || (config.endpoint && typeof config.endpoint !== "string")) {
|
||||
throw new Error("'endpoint' is a required property of the ConnectionConfig.");
|
||||
throw new Error("'endpoint' is a required property of ConnectionConfig.");
|
||||
}
|
||||
if (!config.entityPath || (config.entityPath && typeof config.entityPath !== "string")) {
|
||||
throw new Error("'entityPath' is a required property of the ConnectionConfig.");
|
||||
if (config.entityPath && typeof config.entityPath !== "string") {
|
||||
throw new Error("'entityPath' must be of type 'string'.");
|
||||
}
|
||||
if (options.isEntityPathRequired && !config.entityPath) {
|
||||
throw new Error("'entityPath' is a required property of ConnectionConfig.");
|
||||
}
|
||||
if (!config.sharedAccessKeyName || (config.sharedAccessKeyName && typeof config.sharedAccessKeyName !== "string")) {
|
||||
throw new Error("'sharedAccessKeyName' is a required property of the ConnectionConfig.");
|
||||
throw new Error("'sharedAccessKeyName' is a required property of ConnectionConfig.");
|
||||
}
|
||||
if (!config.sharedAccessKey || (config.sharedAccessKey && typeof config.sharedAccessKey !== "string")) {
|
||||
throw new Error("'sharedAccessKey' is a required property of the ConnectionConfig.");
|
||||
throw new Error("'sharedAccessKey' is a required property of ConnectionConfig.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import * as rhea from "rhea";
|
|||
import * as debugModule from "debug";
|
||||
|
||||
const isBuffer = require("is-buffer");
|
||||
const debug = debugModule("azure:event-hubs:datatransformer");
|
||||
const debug = debugModule("azure:amqp-common:datatransformer");
|
||||
|
||||
export interface DataTransformer {
|
||||
/**
|
||||
|
@ -29,6 +29,7 @@ export class DefaultDataTransformer implements DataTransformer {
|
|||
/**
|
||||
* A function that takes the body property from an EventData object
|
||||
* and returns an encoded body (some form of AMQP type).
|
||||
*
|
||||
* @param {*} body The AMQP message body
|
||||
* @return {DataSection} encodedBody - The encoded AMQP message body as an AMQP Data type
|
||||
* (data section in rhea terms). Section object with following properties:
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { AmqpError, AmqpResponseStatusCode } from "../lib/rhea-promise";
|
||||
|
||||
import { AmqpResponseStatusCode, isAmqpError } from "../rhea-promise";
|
||||
import { AmqpError } from "rhea";
|
||||
/**
|
||||
* Maps the conditions to the numeric AMQP Response status codes.
|
||||
* @enum {ConditionStatusMapper}
|
||||
|
@ -12,6 +12,7 @@ export enum ConditionStatusMapper {
|
|||
"amqp:not-found" = AmqpResponseStatusCode.NotFound,
|
||||
"amqp:not-implemented" = AmqpResponseStatusCode.NotImplemented,
|
||||
"com.microsoft:entity-already-exists" = AmqpResponseStatusCode.Conflict,
|
||||
"com.microsoft:message-lock-lost" = AmqpResponseStatusCode.Gone,
|
||||
"com.microsoft:session-lock-lost" = AmqpResponseStatusCode.Gone,
|
||||
"com.microsoft:no-matching-subscription" = AmqpResponseStatusCode.InternalServerError,
|
||||
"amqp:link:message-size-exceeded" = AmqpResponseStatusCode.Forbidden,
|
||||
|
@ -26,8 +27,7 @@ export enum ConditionStatusMapper {
|
|||
"amqp:link:stolen" = AmqpResponseStatusCode.Gone,
|
||||
"amqp:not-allowed" = AmqpResponseStatusCode.BadRequest,
|
||||
"amqp:unauthorized-access" = AmqpResponseStatusCode.Unauthorized,
|
||||
"amqp:resource-limit-exceeded" = AmqpResponseStatusCode.Forbidden,
|
||||
"com.microsoft:message-lock-lost" = AmqpResponseStatusCode.Gone,
|
||||
"amqp:resource-limit-exceeded" = AmqpResponseStatusCode.Forbidden
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,6 +35,22 @@ export enum ConditionStatusMapper {
|
|||
* @enum {ConditionErrorNameMapper}
|
||||
*/
|
||||
export enum ConditionErrorNameMapper {
|
||||
/**
|
||||
* Error is thrown when trying to access/connect to a disabled messaging entity.
|
||||
*/
|
||||
"com.microsoft:entity-disabled" = "MessagingEntityDisabledError",
|
||||
/**
|
||||
* Error is thrown when the lock on the message is lost.
|
||||
*/
|
||||
"com.microsoft:message-lock-lost" = "MessageLockLostError",
|
||||
/**
|
||||
* Error is thrown when the lock on the Azure ServiceBus session is lost.
|
||||
*/
|
||||
"com.microsoft:session-lock-lost" = "SessionLockLostError",
|
||||
/**
|
||||
* Error is thrown when the Azure ServiceBus session cannot be locked.
|
||||
*/
|
||||
"com.microsoft:session-cannot-be-locked" = "SessionCannotBeLockedError",
|
||||
/**
|
||||
* Error is thrown when an internal server error occured. You may have found a bug?
|
||||
*/
|
||||
|
@ -42,7 +58,7 @@ export enum ConditionErrorNameMapper {
|
|||
/**
|
||||
* Error for signaling general communication errors related to messaging operations.
|
||||
*/
|
||||
"amqp:not-found" = "EventHubsCommunicationError",
|
||||
"amqp:not-found" = "ServiceCommunicationError",
|
||||
/**
|
||||
* Error is thrown when a feature is not implemented yet but the placeholder is present.
|
||||
*/
|
||||
|
@ -52,7 +68,7 @@ export enum ConditionErrorNameMapper {
|
|||
*/
|
||||
"amqp:not-allowed" = "InvalidOperationError",
|
||||
/**
|
||||
* Error is thrown the the Azure Event Hub quota has been exceeded.
|
||||
* Error is thrown the the Azure EventHub/ServiceBus quota has been exceeded.
|
||||
* Quotas are reset periodically, this operation will have to wait until then.
|
||||
* The messaging entity has reached its maximum allowable size.
|
||||
* This can happen if the maximum number of receivers (which is 5) has already
|
||||
|
@ -63,6 +79,10 @@ export enum ConditionErrorNameMapper {
|
|||
* Error is thrown when the connection parameters are wrong and the server refused the connection.
|
||||
*/
|
||||
"amqp:unauthorized-access" = "UnauthorizedError",
|
||||
/**
|
||||
* Error is thrown when the connection parameters are wrong and the server refused the connection.
|
||||
*/
|
||||
"com.microsoft:auth-failed" = "UnauthorizedError",
|
||||
/**
|
||||
* Error is thrown when the service is unavailable. The operation should be retried.
|
||||
*/
|
||||
|
@ -75,6 +95,10 @@ export enum ConditionErrorNameMapper {
|
|||
* Error is thrown when a condition that should have been met in order to execute an operation was not.
|
||||
*/
|
||||
"amqp:precondition-failed" = "PreconditionFailedError",
|
||||
/**
|
||||
* Error is thrown when a condition that should have been met in order to execute an operation was not.
|
||||
*/
|
||||
"com.microsoft:precondition-failed" = "PreconditionFailedError",
|
||||
/**
|
||||
* Error is thrown when data could not be decoded.
|
||||
*/
|
||||
|
@ -167,6 +191,22 @@ export enum ConditionErrorNameMapper {
|
|||
* @enum {ErrorNameConditionMapper}
|
||||
*/
|
||||
export enum ErrorNameConditionMapper {
|
||||
/**
|
||||
* Error is thrown when trying to access/connect to a disabled messaging entity.
|
||||
*/
|
||||
MessagingEntityDisabledError = "com.microsoft:entity-disabled",
|
||||
/**
|
||||
* Error is thrown when the lock on the message is lost.
|
||||
*/
|
||||
MessageLockLostError = "com.microsoft:message-lock-lost",
|
||||
/**
|
||||
* Error is thrown when the lock on the Azure ServiceBus session is lost.
|
||||
*/
|
||||
SessionLockLostError = "com.microsoft:session-lock-lost",
|
||||
/**
|
||||
* Error is thrown when the Azure ServiceBus session cannot be locked.
|
||||
*/
|
||||
SessionCannotBeLockedError = "com.microsoft:session-cannot-be-locked",
|
||||
/**
|
||||
* Error is thrown when an internal server error occured. You may have found a bug?
|
||||
*/
|
||||
|
@ -174,7 +214,7 @@ export enum ErrorNameConditionMapper {
|
|||
/**
|
||||
* Error for signaling general communication errors related to messaging operations.
|
||||
*/
|
||||
EventHubsCommunicationError = "amqp:not-found",
|
||||
ServiceCommunicationError = "amqp:not-found",
|
||||
/**
|
||||
* Error is thrown when a feature is not implemented yet but the placeholder is present.
|
||||
*/
|
||||
|
@ -184,7 +224,7 @@ export enum ErrorNameConditionMapper {
|
|||
*/
|
||||
InvalidOperationError = "amqp:not-allowed",
|
||||
/**
|
||||
* Error is thrown the the Azure Event Hub quota has been exceeded.
|
||||
* Error is thrown the the Azure EventHub/ServiceBus quota has been exceeded.
|
||||
* Quotas are reset periodically, this operation will have to wait until then.
|
||||
* The messaging entity has reached its maximum allowable size.
|
||||
* This can happen if the maximum number of receivers (which is 5) has already
|
||||
|
@ -295,19 +335,19 @@ export enum ErrorNameConditionMapper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Describes the base class for an EventHub Error.
|
||||
* @class {EventHubsError}
|
||||
* Describes the base class for Messaging Error.
|
||||
* @class {MessagingError}
|
||||
* @extends Error
|
||||
*/
|
||||
export class EventHubsError extends Error {
|
||||
export class MessagingError extends Error {
|
||||
/**
|
||||
* @property {string} [condition] The error condition.
|
||||
*/
|
||||
condition?: string;
|
||||
/**
|
||||
* @property {string} name The error name. Default value: "EventHubsError".
|
||||
* @property {string} name The error name. Default value: "MessagingError".
|
||||
*/
|
||||
name: string = "EventHubsError";
|
||||
name: string = "MessagingError";
|
||||
/**
|
||||
* @property {boolean} translated Has the error been translated. Default: true.
|
||||
*/
|
||||
|
@ -330,44 +370,24 @@ export class EventHubsError extends Error {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given error object is like an AmqpError object.
|
||||
* @param err The AmqpError object
|
||||
*/
|
||||
function isAmqpError(err: any): boolean {
|
||||
if (!err || typeof err !== "object") {
|
||||
throw new Error("err is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
let result: boolean = false;
|
||||
if (((err.condition && typeof err.condition === "string") && (err.description && typeof err.description === "string"))
|
||||
|| (err.value && Array.isArray(err.value))
|
||||
|| (err.constructor && err.constructor.name === "c")) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the AQMP error received at the protocol layer or a generic Error into an EventHubsError.
|
||||
* Translates the AQMP error received at the protocol layer or a generic Error into an MessagingError.
|
||||
*
|
||||
* @param {AmqpError} err The amqp error that was received.
|
||||
* @returns {EventHubsError} EventHubsError object.
|
||||
* @returns {MessagingError} MessagingError object.
|
||||
*/
|
||||
export function translate(err: AmqpError | Error): EventHubsError {
|
||||
if ((err as EventHubsError).translated) { // already translated
|
||||
return err as EventHubsError;
|
||||
export function translate(err: AmqpError | Error): MessagingError {
|
||||
if ((err as MessagingError).translated) { // already translated
|
||||
return err as MessagingError;
|
||||
} else if (isAmqpError(err)) { // translate
|
||||
const condition = (err as AmqpError).condition;
|
||||
const description = (err as AmqpError).description as string;
|
||||
const error = new EventHubsError(description);
|
||||
const error = new MessagingError(description);
|
||||
error.info = (err as AmqpError).info;
|
||||
error.condition = condition;
|
||||
if (condition) {
|
||||
if (condition === "com.microsoft:precondition-failed") {
|
||||
error.name = "PreconditionFailedError";
|
||||
} else {
|
||||
error.name = ConditionErrorNameMapper[condition as any] || "EventHubsError";
|
||||
}
|
||||
error.name = ConditionErrorNameMapper[condition as any];
|
||||
}
|
||||
if (!error.name) error.name = "MessagingError";
|
||||
if (description &&
|
||||
(description.includes("status-code: 404") ||
|
||||
description.match(/The messaging entity .* could not be found.*/i) !== null)) {
|
||||
|
@ -380,8 +400,8 @@ export function translate(err: AmqpError | Error): EventHubsError {
|
|||
}
|
||||
return error;
|
||||
} else {
|
||||
// Translate a generic error into EventHubsError.
|
||||
const error = new EventHubsError((err as Error).message);
|
||||
// Translate a generic error into MessagingError.
|
||||
const error = new MessagingError((err as Error).message);
|
||||
return error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
export { Dictionary, Message as AmqpMessage } from "../rhea-promise";
|
||||
export { ConnectionConfig } from "./connectionConfig";
|
||||
export {
|
||||
MessagingError, ErrorNameConditionMapper, ConditionStatusMapper, ConditionErrorNameMapper, translate
|
||||
} from "./errors";
|
||||
export { RequestResponseLink } from "./requestResponseLink";
|
||||
export {
|
||||
CreateConnectionPrameters, open, ServiceBusMessageAnnotations,
|
||||
ServiceBusDeliveryAnnotations, EventHubDeliveryAnnotations, EventHubMessageAnnotations
|
||||
} from "./rpc";
|
||||
export { retry } from "./retry";
|
||||
export { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
|
||||
export { TokenType, TokenProvider, TokenInfo } from "./auth/token";
|
||||
export { SasTokenProvider } from "./auth/sas";
|
||||
export { AadTokenProvider } from "./auth/aad";
|
||||
export { CbsClient } from "./cbs";
|
||||
import * as Constants from "./util/constants";
|
||||
export { Constants };
|
||||
export { MessageHeader } from "./messageHeader";
|
||||
export { MessageProperties } from "./messageProperties";
|
||||
export {
|
||||
delay, Timeout, EventHubConnectionStringModel, executePromisesSequentially,
|
||||
parseConnectionString, IotHubConnectionStringModel, StorageConnectionStringModel, defaultLock,
|
||||
Func, ParsedOutput, getNewAsyncLock, AsyncLockOptions, ServiceBusConnectionStringModel,
|
||||
isIotHubConnectionString
|
||||
} from "./util/utils";
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { MessageHeader as AmqpMessageHeader } from "../rhea-promise";
|
||||
import * as debugModule from "debug";
|
||||
const debug = debugModule("azure:amqp-common:messageHeader");
|
||||
|
||||
/**
|
||||
* Describes the defined set of standard header properties of the message.
|
||||
* @interface MessageHeader
|
||||
*/
|
||||
export interface MessageHeader {
|
||||
/**
|
||||
* @property {boolean} [firstAcquirer] If this value is true, then this message has not been
|
||||
* acquired by any other link. Ifthis value is false, then this message MAY have previously
|
||||
* been acquired by another link or links.
|
||||
*/
|
||||
firstAcquirer?: boolean;
|
||||
/**
|
||||
* @property {number} [deliveryCount] The number of prior unsuccessful delivery attempts.
|
||||
*/
|
||||
deliveryCount?: number;
|
||||
/**
|
||||
* @property {number} [ttl] time to live in ms.
|
||||
*/
|
||||
ttl?: number;
|
||||
/**
|
||||
* @property {boolean} [durable] Specifies durability requirements.
|
||||
*/
|
||||
durable?: boolean;
|
||||
/**
|
||||
* @property {number} [priority] The relative message priority. Higher numbers indicate higher
|
||||
* priority messages.
|
||||
*/
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
export namespace MessageHeader {
|
||||
|
||||
export function toAmqpMessageHeader(props: MessageHeader): AmqpMessageHeader {
|
||||
const amqpHeader: AmqpMessageHeader = {
|
||||
delivery_count: props.deliveryCount,
|
||||
durable: props.durable,
|
||||
first_acquirer: props.firstAcquirer,
|
||||
priority: props.priority,
|
||||
ttl: props.ttl
|
||||
};
|
||||
debug("To AmqpMessageHeader: %O", amqpHeader);
|
||||
return amqpHeader;
|
||||
}
|
||||
|
||||
export function fromAmqpMessageProperties(props: AmqpMessageHeader): MessageHeader {
|
||||
const msgHeader: MessageHeader = {
|
||||
deliveryCount: props.delivery_count,
|
||||
durable: props.durable,
|
||||
firstAcquirer: props.first_acquirer,
|
||||
priority: props.priority,
|
||||
ttl: props.ttl
|
||||
};
|
||||
debug("From AmqpMessageHeader: %O", msgHeader);
|
||||
return msgHeader;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { MessageProperties as AmqpMessageProperties } from "../rhea-promise";
|
||||
import * as debugModule from "debug";
|
||||
const debug = debugModule("azure:amqp-common:messageProperties");
|
||||
|
||||
/**
|
||||
* Describes the defined set of standard properties of the message.
|
||||
* @interface MessageProperties
|
||||
*/
|
||||
export interface MessageProperties {
|
||||
/**
|
||||
* @property {string | number | Buffer} [messageId] The application message identifier that uniquely idenitifes a message.
|
||||
* The user is responsible for making sure that this is unique in the given context. Guids usually make a good fit.
|
||||
*/
|
||||
messageId?: string | number | Buffer;
|
||||
/**
|
||||
* @property {string} [replyTo] The address of the node to send replies to.
|
||||
*/
|
||||
replyTo?: string;
|
||||
/**
|
||||
* @property {string} [to] The address of the node the message is destined for.
|
||||
*/
|
||||
to?: string;
|
||||
/**
|
||||
* @property {string | number | Buffer} [correlationId] The id that can be used to mark or identify messages between clients.
|
||||
*/
|
||||
correlationId?: string | number | Buffer;
|
||||
/**
|
||||
* @property {string} [contentType] MIME type for the message.
|
||||
*/
|
||||
contentType?: string;
|
||||
/**
|
||||
* @property {string} [contentEncoding] The content-encoding property is used as a modifier to the content-type.
|
||||
* When present, its valueindicates what additional content encodings have been applied to the application-data.
|
||||
*/
|
||||
contentEncoding?: string;
|
||||
/**
|
||||
* @property {number} [absoluteExpiryTime] The time when this message is considered expired.
|
||||
*/
|
||||
absoluteExpiryTime?: number;
|
||||
/**
|
||||
* @property {number} [creationTime] The time this message was created.
|
||||
*/
|
||||
creationTime?: number;
|
||||
/**
|
||||
* @property {string} [groupId] The group this message belongs to.
|
||||
*/
|
||||
groupId?: string;
|
||||
/**
|
||||
* @property {number} [groupSequence] The sequence number of this message with its group.
|
||||
*/
|
||||
groupSequence?: number;
|
||||
/**
|
||||
* @property {string} [replyToGroupId] The group the reply message belongs to.
|
||||
*/
|
||||
replyToGroupId?: string;
|
||||
/**
|
||||
* @property {string} [subject] A common field for summary information about the message
|
||||
* content and purpose.
|
||||
*/
|
||||
subject?: string;
|
||||
/**
|
||||
* @property {string} [userId] The identity of the user responsible for producing the message.
|
||||
*/
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export namespace MessageProperties {
|
||||
|
||||
export function toAmqpMessageProperties(props: MessageProperties): AmqpMessageProperties {
|
||||
const amqpProperties: AmqpMessageProperties = {
|
||||
absolute_expiry_time: props.absoluteExpiryTime,
|
||||
content_encoding: props.contentEncoding,
|
||||
content_type: props.contentType,
|
||||
correlation_id: props.correlationId,
|
||||
creation_time: props.creationTime,
|
||||
group_id: props.groupId,
|
||||
group_sequence: props.groupSequence,
|
||||
message_id: props.messageId,
|
||||
reply_to: props.replyTo,
|
||||
reply_to_group_id: props.replyToGroupId,
|
||||
subject: props.subject,
|
||||
to: props.to,
|
||||
user_id: props.userId
|
||||
};
|
||||
debug("To AmqpMessageProperties: %O", amqpProperties);
|
||||
return amqpProperties;
|
||||
}
|
||||
|
||||
export function fromAmqpMessageProperties(props: AmqpMessageProperties): MessageProperties {
|
||||
const msgProperties: MessageProperties = {
|
||||
absoluteExpiryTime: props.absolute_expiry_time,
|
||||
contentEncoding: props.content_encoding,
|
||||
contentType: props.content_type,
|
||||
correlationId: props.correlation_id,
|
||||
creationTime: props.creation_time,
|
||||
groupId: props.group_id,
|
||||
groupSequence: props.group_sequence,
|
||||
messageId: props.message_id,
|
||||
replyTo: props.reply_to,
|
||||
replyToGroupId: props.reply_to_group_id,
|
||||
subject: props.subject,
|
||||
to: props.to,
|
||||
userId: props.user_id
|
||||
};
|
||||
debug("From AmqpMessageProperties: %O", msgProperties);
|
||||
return msgProperties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as uuid from "uuid/v4";
|
||||
import * as Constants from "./util/constants";
|
||||
import * as debugModule from "debug";
|
||||
import { retry } from "./retry";
|
||||
import {
|
||||
Session, Connection, Sender, Receiver, Message, EventContext, AmqpError, ReqResLink,
|
||||
SenderOptions, ReceiverOptions, ReceiverEvents
|
||||
} from "../rhea-promise";
|
||||
import { translate, ConditionStatusMapper } from "./errors";
|
||||
|
||||
const debug = debugModule("azure:amqp-common:reqreslink");
|
||||
|
||||
export class RequestResponseLink implements ReqResLink {
|
||||
|
||||
constructor(public session: Session, public sender: Sender, public receiver: Receiver) {
|
||||
this.session = session;
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
return this.session.connection;
|
||||
}
|
||||
|
||||
isOpen(): boolean {
|
||||
return this.session.isOpen() && this.sender.isOpen() && this.receiver.isOpen();
|
||||
}
|
||||
|
||||
sendRequest<T>(request: Message, timeoutInSeconds?: number): Promise<T> {
|
||||
if (!request) {
|
||||
throw new Error("request is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
if (!request.message_id) request.message_id = uuid();
|
||||
|
||||
if (!timeoutInSeconds) {
|
||||
timeoutInSeconds = 10;
|
||||
}
|
||||
|
||||
const sendRequestPromise: Promise<T> = new Promise((resolve: any, reject: any) => {
|
||||
let waitTimer: any;
|
||||
let timeOver: boolean = false;
|
||||
|
||||
const messageCallback = (context: EventContext) => {
|
||||
// remove the event listener as this will be registered next time when someone makes a request.
|
||||
this.receiver.removeHandler(ReceiverEvents.message, messageCallback);
|
||||
const code: number = context.message!.application_properties![Constants.statusCode];
|
||||
const desc: string = context.message!.application_properties![Constants.statusDescription];
|
||||
const errorCondition: string | undefined = context.message!.application_properties![Constants.errorCondition];
|
||||
const responseCorrelationId = context.message!.correlation_id;
|
||||
debug("[%s] %s response: ", this.connection.id, request.to || "$management", context.message);
|
||||
if (code > 199 && code < 300) {
|
||||
if (request.message_id === responseCorrelationId || request.correlation_id === responseCorrelationId) {
|
||||
if (!timeOver) {
|
||||
clearTimeout(waitTimer);
|
||||
}
|
||||
debug("[%s] request-messageId | '%s' == '%s' | response-correlationId.",
|
||||
this.connection.id, request.message_id, responseCorrelationId);
|
||||
return resolve(context.message!.body);
|
||||
} else {
|
||||
debug("[%s] request-messageId | '%s' != '%s' | response-correlationId. " +
|
||||
"Hence dropping this response and waiting for the next one.",
|
||||
this.connection.id, request.message_id, responseCorrelationId);
|
||||
}
|
||||
} else {
|
||||
const condition = errorCondition || ConditionStatusMapper[code] || "amqp:internal-error";
|
||||
const e: AmqpError = {
|
||||
condition: condition,
|
||||
description: desc
|
||||
};
|
||||
return reject(translate(e));
|
||||
}
|
||||
};
|
||||
|
||||
const actionAfterTimeout = () => {
|
||||
timeOver = true;
|
||||
this.receiver.removeHandler(ReceiverEvents.message, messageCallback);
|
||||
const address = this.receiver.address || "address";
|
||||
const desc: string = `The request with message_id "${request.message_id}" to "${address}" ` +
|
||||
`endpoint timed out. Please try again later.`;
|
||||
const e: AmqpError = {
|
||||
condition: ConditionStatusMapper[408],
|
||||
description: desc
|
||||
};
|
||||
return reject(translate(e));
|
||||
};
|
||||
|
||||
this.receiver.registerHandler(ReceiverEvents.message, messageCallback);
|
||||
waitTimer = setTimeout(actionAfterTimeout, timeoutInSeconds! * 1000);
|
||||
debug("[%s] %s request sent: %O", this.connection.id, request.to || "$managment", request);
|
||||
this.sender.send(request);
|
||||
});
|
||||
|
||||
return retry<T>(() => sendRequestPromise);
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.sender.close();
|
||||
await this.receiver.close();
|
||||
await this.session.close();
|
||||
}
|
||||
|
||||
static async create(connection: Connection, senderOptions: SenderOptions, receiverOptions: ReceiverOptions): Promise<RequestResponseLink> {
|
||||
if (!connection) {
|
||||
throw new Error(`Please provide a connection to create the sender/receiver link on the same session.`);
|
||||
}
|
||||
if (!senderOptions) {
|
||||
throw new Error(`Please provide sender options.`);
|
||||
}
|
||||
if (!receiverOptions) {
|
||||
throw new Error(`Please provide receiver options.`);
|
||||
}
|
||||
const session = await connection.createSession();
|
||||
const sender = await session.createSender(senderOptions);
|
||||
const receiver = await session.createReceiver(receiverOptions);
|
||||
debug("[%s] Successfully created the sender and receiver links on the same session.", connection.id);
|
||||
return new RequestResponseLink(session, sender, receiver);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { translate, EventHubsError } from "./errors";
|
||||
import { translate, MessagingError } from "./errors";
|
||||
import { delay } from ".";
|
||||
import * as debugModule from "debug";
|
||||
const debug = debugModule("azure:event-hubs:retry");
|
||||
const debug = debugModule("azure:amqp-common:retry");
|
||||
|
||||
function isDelivery(obj: any): boolean {
|
||||
let result: boolean = false;
|
||||
|
@ -40,7 +40,7 @@ export async function retry<T>(operation: () => Promise<T>, times?: number, dela
|
|||
|
||||
if (!times) times = 3;
|
||||
if (!delayInSeconds) delayInSeconds = 15;
|
||||
let lastError: EventHubsError | undefined;
|
||||
let lastError: MessagingError | undefined;
|
||||
let result: any;
|
||||
let success = false;
|
||||
for (let i = 0; i < times; i++) {
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as os from "os";
|
||||
import * as process from "process";
|
||||
import * as debugModule from "debug";
|
||||
import { defaultLock } from "./util/utils";
|
||||
import { ConnectionOptions, Connection, DeliveryAnnotations, MessageAnnotations } from "../rhea-promise";
|
||||
import * as Constants from "./util/constants";
|
||||
import { ConnectionConfig } from ".";
|
||||
const debug = debugModule("azure:amqp-common:rpc");
|
||||
|
||||
/**
|
||||
* Describes the delivery annotations.
|
||||
* @interface
|
||||
*/
|
||||
export interface EventHubDeliveryAnnotations extends DeliveryAnnotations {
|
||||
/**
|
||||
* @property {string} [last_enqueued_offset] The offset of the last event.
|
||||
*/
|
||||
last_enqueued_offset?: string;
|
||||
/**
|
||||
* @property {number} [last_enqueued_sequence_number] The sequence number of the last event.
|
||||
*/
|
||||
last_enqueued_sequence_number?: number;
|
||||
/**
|
||||
* @property {number} [last_enqueued_time_utc] The enqueued time of the last event.
|
||||
*/
|
||||
last_enqueued_time_utc?: number;
|
||||
/**
|
||||
* @property {number} [runtime_info_retrieval_time_utc] The retrieval time of the last event.
|
||||
*/
|
||||
runtime_info_retrieval_time_utc?: number;
|
||||
/**
|
||||
* @property {string} Any unknown delivery annotations.
|
||||
*/
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the delivery annotations.
|
||||
* @interface
|
||||
*/
|
||||
export interface ServiceBusDeliveryAnnotations extends DeliveryAnnotations {
|
||||
/**
|
||||
* @property {string} [last_enqueued_offset] The offset of the last event.
|
||||
*/
|
||||
last_enqueued_offset?: string;
|
||||
/**
|
||||
* @property {number} [last_enqueued_sequence_number] The sequence number of the last event.
|
||||
*/
|
||||
last_enqueued_sequence_number?: number;
|
||||
/**
|
||||
* @property {number} [last_enqueued_time_utc] The enqueued time of the last event.
|
||||
*/
|
||||
last_enqueued_time_utc?: number;
|
||||
/**
|
||||
* @property {number} [runtime_info_retrieval_time_utc] The retrieval time of the last event.
|
||||
*/
|
||||
runtime_info_retrieval_time_utc?: number;
|
||||
/**
|
||||
* @property {string} Any unknown delivery annotations.
|
||||
*/
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map containing message attributes that will be held in the message header.
|
||||
*/
|
||||
export interface EventHubMessageAnnotations extends MessageAnnotations {
|
||||
/**
|
||||
* @property {string | null} [x-opt-partition-key] Annotation for the partition key set for the event.
|
||||
*/
|
||||
"x-opt-partition-key"?: string | null;
|
||||
/**
|
||||
* @property {number} [x-opt-sequence-number] Annontation for the sequence number of the event.
|
||||
*/
|
||||
"x-opt-sequence-number"?: number;
|
||||
/**
|
||||
* @property {number} [x-opt-enqueued-time] Annotation for the enqueued time of the event.
|
||||
*/
|
||||
"x-opt-enqueued-time"?: number;
|
||||
/**
|
||||
* @property {string} [x-opt-offset] Annotation for the offset of the event.
|
||||
*/
|
||||
"x-opt-offset"?: string;
|
||||
/**
|
||||
* @property {any} Any other annotation that can be added to the message.
|
||||
*/
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map containing message attributes that will be held in the message header.
|
||||
*/
|
||||
export interface ServiceBusMessageAnnotations extends MessageAnnotations {
|
||||
/**
|
||||
* @property {string | null} [x-opt-partition-key] Annotation for the partition key set for the event.
|
||||
*/
|
||||
"x-opt-partition-key"?: string | null;
|
||||
/**
|
||||
* @property {number} [x-opt-sequence-number] Annontation for the sequence number of the event.
|
||||
*/
|
||||
"x-opt-sequence-number"?: number;
|
||||
/**
|
||||
* @property {number} [x-opt-enqueued-time] Annotation for the enqueued time of the event.
|
||||
*/
|
||||
"x-opt-enqueued-time"?: number;
|
||||
/**
|
||||
* @property {string} [x-opt-offset] Annotation for the offset of the event.
|
||||
*/
|
||||
"x-opt-offset"?: string;
|
||||
/**
|
||||
* @property {string} [x-opt-locked-until] Annotation for the message being locked until.
|
||||
*/
|
||||
"x-opt-locked-until"?: Date | number;
|
||||
}
|
||||
|
||||
export interface CreateConnectionPrameters {
|
||||
config: ConnectionConfig;
|
||||
userAgent: string;
|
||||
packageVersion: string;
|
||||
useSaslPlain?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the AMQP connection to the EventHub/ServiceBus for this client, returning a promise
|
||||
* that will be resolved when the connection is completed.
|
||||
*
|
||||
* @param {ConnectionContext} context The connection context.
|
||||
* @param {boolean} [useSaslPlain] True for using sasl plain mode for authentication, false otherwise.
|
||||
* @returns {Promise<Connection>} The Connection object.
|
||||
*/
|
||||
export async function open(params: CreateConnectionPrameters): Promise<Connection> {
|
||||
try {
|
||||
return await defaultLock.acquire("connect", () => { return _open(params); });
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function _open(params: CreateConnectionPrameters): Promise<Connection> {
|
||||
const connectOptions: ConnectionOptions = {
|
||||
transport: Constants.TLS,
|
||||
host: params.config.host,
|
||||
hostname: params.config.host,
|
||||
username: params.config.sharedAccessKeyName,
|
||||
port: 5671,
|
||||
reconnect_limit: Constants.reconnectLimit,
|
||||
properties: {
|
||||
product: "MSJSClient",
|
||||
version: params.packageVersion,
|
||||
platform: `(${os.arch()}-${os.type()}-${os.release()})`,
|
||||
framework: `Node/${process.version}`,
|
||||
"user-agent": params.userAgent
|
||||
}
|
||||
};
|
||||
if (params.useSaslPlain) {
|
||||
connectOptions.password = params.config.sharedAccessKey;
|
||||
}
|
||||
debug("Dialing the amqp connection with options.", connectOptions);
|
||||
const connection = await new Connection(connectOptions).open();
|
||||
debug("Successfully established the amqp connection '%s'.", connection.id);
|
||||
return connection;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
export const partitionKey = "x-opt-partition-key";
|
||||
export const sequenceNumber = "x-opt-sequence-number";
|
||||
export const enqueueSequenceNumber = "x-opt-enqueue-sequence-number";
|
||||
export const enqueuedTime = "x-opt-enqueued-time";
|
||||
export const scheduledEnqueueTime = "x-opt-scheduled-enqueue-time";
|
||||
export const offset = "x-opt-offset";
|
||||
export const lockedUntil = "x-opt-locked-until";
|
||||
export const partitionIdName = "x-opt-partition-id";
|
||||
export const publisher = "x-opt-publisher-name";
|
||||
export const viaPartitionKey = "x-opt-via-partition-key";
|
||||
export const deadLetterSource = "x-opt-deadletter-source";
|
||||
export const enqueuedTimeAnnotation = `amqp.annotation.${enqueuedTime}`;
|
||||
export const offsetAnnotation = `amqp.annotation.${offset}`;
|
||||
export const sequenceNumberAnnotation = `amqp.annotation.${sequenceNumber}`;
|
||||
export const guidSize = 16;
|
||||
export const message = "message";
|
||||
export const error = "error";
|
||||
export const statusCode = "status-code";
|
||||
export const statusDescription = "status-description";
|
||||
export const errorCondition = "error-condition";
|
||||
export const management = "$management";
|
||||
export const partition = "partition";
|
||||
export const partitionId = "partitionId";
|
||||
export const readOperation = "READ";
|
||||
export const TLS = "tls";
|
||||
export const establishConnection = "establishConnection";
|
||||
export const defaultConsumerGroup = "$default";
|
||||
export const eventHub = "eventhub";
|
||||
export const cbsEndpoint = "$cbs";
|
||||
export const cbsReplyTo = "cbs";
|
||||
export const operationPutToken = "put-token";
|
||||
export const aadEventHubsAudience = "https://eventhubs.azure.net/";
|
||||
export const maxUserAgentLength = 128;
|
||||
export const vendorString = "com.microsoft";
|
||||
export const attachEpoch = `${vendorString}:epoch`;
|
||||
export const receiverIdentifierName = `${vendorString}:receiver-name`;
|
||||
export const enableReceiverRuntimeMetricName = `${vendorString}:enable-receiver-runtime-metric`;
|
||||
export const timespan = `${vendorString}:timespan`;
|
||||
export const uri = `${vendorString}:uri`;
|
||||
export const dateTimeOffset = `${vendorString}:datetime-offset`;
|
||||
export const sessionFilterName = `${vendorString}:session-filter`;
|
||||
export const receiverError = "receiver_error";
|
||||
export const senderError = "sender_error";
|
||||
export const sessionError = "session_error";
|
||||
export const connectionError = "connection_error";
|
||||
export const defaultOperationTimeoutInSeconds = 60;
|
||||
export const managementRequestKey = "managementRequest";
|
||||
export const negotiateCbsKey = "negotiateCbs";
|
||||
export const negotiateClaim = "negotiateClaim";
|
||||
export const ensureContainerAndBlob = "ensureContainerAndBlob";
|
||||
export const defaultPrefetchCount = 1000;
|
||||
export const reconnectLimit = 100;
|
||||
export const maxMessageIdLength = 128;
|
||||
export const maxPartitionKeyLength = 128;
|
||||
export const maxSessionIdLength = 128;
|
||||
export const pathDelimiter = "/";
|
||||
export const ruleNameMaximumLength = 50;
|
||||
export const maximumSqlFilterStatementLength = 1024;
|
||||
export const maximumSqlRuleActionStatementLength = 1024;
|
||||
export const maxDeadLetterReasonLength = 4096;
|
||||
// https://stackoverflow.com/questions/11526504/minimum-and-maximum-date for js
|
||||
// However we are setting this to the TimeSpan.MaxValue of C#.
|
||||
export const maxDurationValue = 922337203685477;
|
||||
export const minDurationValue = -922337203685477;
|
||||
// https://github.com/Azure/azure-amqp/blob/master/Microsoft.Azure.Amqp/Amqp/AmqpConstants.cs#L47
|
||||
export const maxAbsoluteExpiryTime = new Date("9999-12-31T07:59:59.000Z").getTime();
|
|
@ -23,6 +23,14 @@ export interface AsyncLockOptions {
|
|||
Promise?: any;
|
||||
}
|
||||
|
||||
export interface ServiceBusConnectionStringModel {
|
||||
Endpoint: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
EntityPath?: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
export interface EventHubConnectionStringModel {
|
||||
Endpoint: string;
|
||||
SharedAccessKeyName: string;
|
||||
|
@ -167,3 +175,21 @@ export function isIotHubConnectionString(connectionString: string): boolean {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function setIfDefined(obj: any, key: string, value: any): void {
|
||||
if (value !== undefined) {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyType(value: any, type: 'string' | 'number'): void {
|
||||
if (value != undefined && typeof value !== type) {
|
||||
throw new TypeError(`Invalid type provided. Value must be a ${type}.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyClass(value: any, clazz: Function, className: string): void {
|
||||
if (value != undefined && !(value instanceof clazz)) {
|
||||
throw new TypeError(`Invalid type provided. Value must be an instance of ${className}.`);
|
||||
}
|
||||
}
|
|
@ -2,13 +2,12 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as debugModule from "debug";
|
||||
import { ReceiverEvents, EventContext, OnAmqpEvent } from "./rhea-promise";
|
||||
import { ReceiveOptions, EventData } from ".";
|
||||
import { EventHubReceiver } from "./eventHubReceiver";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { translate } from "./errors";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import * as Constants from "./util/constants";
|
||||
import { Func } from "./util/utils";
|
||||
import { translate, Func, Constants } from "./amqp-common";
|
||||
|
||||
const debug = debugModule("azure:event-hubs:receiverbatching");
|
||||
|
||||
|
||||
|
@ -63,15 +62,15 @@ export class BatchingReceiver extends EventHubReceiver {
|
|||
const eventDatas: EventData[] = [];
|
||||
let timeOver = false;
|
||||
return new Promise<EventData[]>((resolve, reject) => {
|
||||
let onReceiveMessage: rheaPromise.OnAmqpEvent;
|
||||
let onReceiveError: rheaPromise.OnAmqpEvent;
|
||||
let onReceiveMessage: OnAmqpEvent;
|
||||
let onReceiveError: OnAmqpEvent;
|
||||
let waitTimer: any;
|
||||
let actionAfterWaitTimeout: Func<void, void>;
|
||||
// Final action to be performed after maxMessageCount is reached or the maxWaitTime is over.
|
||||
const finalAction = (timeOver: boolean, data?: EventData) => {
|
||||
// Resetting the mode. Now anyone can call start() or receive() again.
|
||||
this._receiver.removeListener(Constants.receiverError, onReceiveError);
|
||||
this._receiver.removeListener(Constants.message, onReceiveMessage);
|
||||
this._receiver!.removeHandler(ReceiverEvents.receiverError, onReceiveError);
|
||||
this._receiver!.removeHandler(ReceiverEvents.message, onReceiveMessage);
|
||||
if (!data) {
|
||||
data = eventDatas.length ? eventDatas[eventDatas.length - 1] : undefined;
|
||||
}
|
||||
|
@ -94,7 +93,7 @@ export class BatchingReceiver extends EventHubReceiver {
|
|||
};
|
||||
|
||||
// Action to be performed on the "message" event.
|
||||
onReceiveMessage = (context: rheaPromise.EventContext) => {
|
||||
onReceiveMessage = (context: EventContext) => {
|
||||
const data: EventData = EventData.fromAmqpMessage(context.message!);
|
||||
data.body = this._context.dataTransformer.decode(context.message!.body);
|
||||
if (eventDatas.length <= maxMessageCount) {
|
||||
|
@ -106,9 +105,9 @@ export class BatchingReceiver extends EventHubReceiver {
|
|||
};
|
||||
|
||||
// Action to be taken when an error is received.
|
||||
onReceiveError = (context: rheaPromise.EventContext) => {
|
||||
this._receiver.removeListener(Constants.receiverError, onReceiveError);
|
||||
this._receiver.removeListener(Constants.message, onReceiveMessage);
|
||||
onReceiveError = (context: EventContext) => {
|
||||
this._receiver!.removeHandler(ReceiverEvents.receiverError, onReceiveError);
|
||||
this._receiver!.removeHandler(ReceiverEvents.message, onReceiveMessage);
|
||||
const error = translate(context.receiver!.error!);
|
||||
debug("[%s] Receiver '%s' received an error:\n%O", this._context.connectionId, this.name, error);
|
||||
if (waitTimer) {
|
||||
|
@ -120,21 +119,21 @@ export class BatchingReceiver extends EventHubReceiver {
|
|||
const addCreditAndSetTimer = (reuse?: boolean) => {
|
||||
debug("[%s] Receiver '%s', adding credit for receiving %d messages.",
|
||||
this._context.connectionId, this.name, maxMessageCount);
|
||||
this._receiver.add_credit(maxMessageCount);
|
||||
this._receiver!.addCredit(maxMessageCount);
|
||||
let msg: string = "[%s] Setting the wait timer for %d seconds for receiver '%s'.";
|
||||
if (reuse) msg += " Receiver link already present, hence reusing it.";
|
||||
debug(msg, this._context.connectionId, maxWaitTimeInSeconds, this.name);
|
||||
waitTimer = setTimeout(actionAfterWaitTimeout, (maxWaitTimeInSeconds as number) * 1000);
|
||||
};
|
||||
|
||||
if (!this._isOpen()) {
|
||||
if (!this.isOpen()) {
|
||||
debug("[%s] Receiver '%s', setting the prefetch count to 0.", this._context.connectionId, this.name);
|
||||
this.prefetchCount = 0;
|
||||
this._init(onReceiveMessage, onReceiveError).then(() => addCreditAndSetTimer()).catch(reject);
|
||||
} else {
|
||||
addCreditAndSetTimer(true);
|
||||
this._receiver.on(Constants.message, onReceiveMessage);
|
||||
this._receiver.on(Constants.receiverError, onReceiveError);
|
||||
this._receiver!.registerHandler(ReceiverEvents.message, onReceiveMessage);
|
||||
this._receiver!.registerHandler(ReceiverEvents.receiverError, onReceiveError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as debugModule from "debug";
|
||||
import * as uuid from "uuid/v4";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { defaultLock } from "./util/utils";
|
||||
import { defaultLock } from "./amqp-common";
|
||||
const debug = debugModule("azure:event-hubs:clientEntity");
|
||||
|
||||
export interface ClientEntityOptions {
|
||||
|
@ -89,7 +89,7 @@ export class ClientEntity {
|
|||
* Creates a new ClientEntity instance.
|
||||
* @constructor
|
||||
* @param {ConnectionContext} context The connection context.
|
||||
* @param {string} [name] Name of the entity.
|
||||
* @param {ClientEntityOptions} [options] Options that can be provided while creating the ClientEntity.
|
||||
*/
|
||||
constructor(context: ConnectionContext, options?: ClientEntityOptions) {
|
||||
if (!options) options = {};
|
||||
|
@ -128,6 +128,10 @@ export class ClientEntity {
|
|||
await defaultLock.acquire(this._context.cbsSession!.cbsLock,
|
||||
() => { return this._context.cbsSession!.init(); });
|
||||
const tokenObject = await this._context.tokenProvider.getToken(this.audience);
|
||||
if (!this._context.connection) {
|
||||
this._context.connection = this._context.cbsSession!.connection;
|
||||
this._context.connectionId = this._context.cbsSession!.connection!.id;
|
||||
}
|
||||
debug("[%s] %s: calling negotiateClaim for audience '%s'.",
|
||||
this._context.connectionId, this.type, this.audience);
|
||||
// Acquire the lock to negotiate the CBS claim.
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
|
||||
import * as debugModule from "debug";
|
||||
import * as uuid from "uuid/v4";
|
||||
import * as Constants from "./util/constants";
|
||||
import { ConnectionConfig } from ".";
|
||||
import { packageJsonInfo } from "./util/constants";
|
||||
import { EventHubReceiver } from "./eventHubReceiver";
|
||||
import { EventHubSender } from "./eventHubSender";
|
||||
import { TokenProvider } from "./auth/token";
|
||||
import {
|
||||
TokenProvider, CbsClient, DataTransformer, DefaultDataTransformer, SasTokenProvider,
|
||||
Constants, ConnectionConfig
|
||||
} from "./amqp-common";
|
||||
import { ManagementClient, ManagementClientOptions } from "./managementClient";
|
||||
import { CbsClient } from "./cbs";
|
||||
import { SasTokenProvider } from "./auth/sas";
|
||||
import { ClientOptions } from "./eventHubClient";
|
||||
import { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
|
||||
import { Connection, Dictionary } from "./rhea-promise";
|
||||
|
||||
const debug = debugModule("azure:event-hubs:connectionContext");
|
||||
|
||||
|
@ -27,9 +27,9 @@ export interface ConnectionContext {
|
|||
*/
|
||||
readonly config: ConnectionConfig;
|
||||
/**
|
||||
* @property {any} [connection] The underlying AMQP connection.
|
||||
* @property {Connection} [connection] The underlying AMQP connection.
|
||||
*/
|
||||
connection?: any;
|
||||
connection?: Connection;
|
||||
/**
|
||||
* @property {string} [connectionId] The amqp connection id that uniquely identifies the connection within a process.
|
||||
*/
|
||||
|
@ -47,11 +47,11 @@ export interface ConnectionContext {
|
|||
/**
|
||||
* @property {Dictionary<EventHubReceiver>} receivers A dictionary of the EventHub Receivers associated with this client.
|
||||
*/
|
||||
receivers: { [x: string]: EventHubReceiver };
|
||||
receivers: Dictionary<EventHubReceiver>;
|
||||
/**
|
||||
* @property {Dictionary<EventHubSender>} senders A dictionary of the EventHub Senders associated with this client.
|
||||
*/
|
||||
senders: { [x: string]: EventHubSender };
|
||||
senders: Dictionary<EventHubSender>;
|
||||
/**
|
||||
* @property {ManagementClient} managementSession A reference to the management session ($management endpoint) on
|
||||
* the underlying amqp connection for the EventHub Client.
|
||||
|
@ -87,8 +87,8 @@ export namespace ConnectionContext {
|
|||
export const userAgent: string = "/js-event-hubs";
|
||||
|
||||
export function create(config: ConnectionConfig, options?: ConnectionContextOptions): ConnectionContext {
|
||||
ConnectionConfig.validate(config);
|
||||
if (!options) options = {};
|
||||
ConnectionConfig.validate(config, { isEntityPathRequired: true });
|
||||
const context: ConnectionContext = {
|
||||
connectionLock: `${Constants.establishConnection}-${uuid()}`,
|
||||
negotiateClaimLock: `${Constants.negotiateClaim}-${uuid()}`,
|
||||
|
@ -99,7 +99,8 @@ export namespace ConnectionContext {
|
|||
receivers: {},
|
||||
dataTransformer: options.dataTransformer || new DefaultDataTransformer()
|
||||
};
|
||||
context.cbsSession = new CbsClient(context);
|
||||
const packageVersion = packageJsonInfo.version;
|
||||
context.cbsSession = new CbsClient(config, packageVersion, userAgent);
|
||||
const mOptions: ManagementClientOptions = {
|
||||
address: options.managementSessionAddress,
|
||||
audience: options.managementSessionAudience
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as Constants from "./util/constants";
|
||||
|
||||
import {
|
||||
Message, MessageProperties, MessageHeader, EventHubMessageAnnotations, Dictionary
|
||||
Message, MessageProperties, MessageHeader, Dictionary, messageHeader, messageProperties
|
||||
} from "./rhea-promise";
|
||||
import { EventHubMessageAnnotations, Constants } from "./amqp-common";
|
||||
|
||||
/**
|
||||
* Describes the structure of an event to be sent or received from the EventHub.
|
||||
|
@ -71,16 +70,6 @@ export interface EventData {
|
|||
_raw_amqp_mesage?: Message;
|
||||
}
|
||||
|
||||
export const messageProperties: string[] = [
|
||||
"message_id", "reply_to", "to", "correlation_id", "content_type", "absolute_expiry_time",
|
||||
"group_id", "group_sequence", "reply_to_group_id", "content_encoding", "creation_time", "subject",
|
||||
"user_id"
|
||||
];
|
||||
|
||||
export const messageHeader: string[] = [
|
||||
"first_acquirer", "delivery_count", "ttl", "durable", "priority"
|
||||
];
|
||||
|
||||
/**
|
||||
* Describes the methods on the EventData interface.
|
||||
* @module EventData
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as debugModule from "debug";
|
||||
import { closeConnection } from "./rhea-promise";
|
||||
import { Delivery } from "rhea";
|
||||
import { ApplicationTokenCredentials, DeviceTokenCredentials, UserTokenCredentials, MSITokenCredentials } from "ms-rest-azure";
|
||||
import { ConnectionConfig, OnMessage, OnError, EventData, EventHubsError, DataTransformer } from ".";
|
||||
import { Delivery } from "./rhea-promise";
|
||||
import {
|
||||
ApplicationTokenCredentials, DeviceTokenCredentials, UserTokenCredentials, MSITokenCredentials
|
||||
} from "ms-rest-azure";
|
||||
import {
|
||||
ConnectionConfig, MessagingError, DataTransformer, TokenProvider,
|
||||
AadTokenProvider
|
||||
} from "./amqp-common";
|
||||
import { OnMessage, OnError } from "./eventHubReceiver";
|
||||
import { EventData } from "./eventData";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { TokenProvider } from "./auth/token";
|
||||
import { AadTokenProvider } from "./auth/aad";
|
||||
import { EventHubPartitionRuntimeInformation, EventHubRuntimeInformation } from "./managementClient";
|
||||
import { EventPosition } from "./eventPosition";
|
||||
import { EventHubSender } from "./eventHubSender";
|
||||
|
@ -113,7 +117,7 @@ export class EventHubClient {
|
|||
*/
|
||||
async close(): Promise<any> {
|
||||
try {
|
||||
if (this._context.connection) {
|
||||
if (this._context.connection && this._context.connection.isOpen()) {
|
||||
// Close all the senders.
|
||||
for (const sender of Object.values(this._context.senders)) {
|
||||
await sender.close();
|
||||
|
@ -126,7 +130,7 @@ export class EventHubClient {
|
|||
await this._context.cbsSession!.close();
|
||||
// Close the management session
|
||||
await this._context.managementSession!.close();
|
||||
await closeConnection(this._context.connection);
|
||||
await this._context.connection.close();
|
||||
debug("Closed the amqp connection '%s' on the client.", this._context.connectionId);
|
||||
this._context.connection = undefined;
|
||||
}
|
||||
|
@ -231,7 +235,7 @@ export class EventHubClient {
|
|||
throw new Error("'partitionId' is a required parameter and must be of type: 'string' | 'number'.");
|
||||
}
|
||||
const bReceiver = BatchingReceiver.create(this._context, partitionId, options);
|
||||
let error: EventHubsError | undefined;
|
||||
let error: MessagingError | undefined;
|
||||
let result: EventData[] = [];
|
||||
try {
|
||||
result = await bReceiver.receive(maxMessageCount, maxWaitTimeInSeconds);
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import { translate } from "./errors";
|
||||
import * as Constants from "./util/constants";
|
||||
import { ReceiveOptions, EventData, EventHubsError } from ".";
|
||||
import { Receiver, OnAmqpEvent, EventContext, ReceiverOptions } from "./rhea-promise";
|
||||
import { translate, Constants, MessagingError } from "./amqp-common";
|
||||
import { ReceiveOptions, EventData } from ".";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { ClientEntity } from "./clientEntity";
|
||||
|
||||
|
@ -47,7 +46,7 @@ export type OnMessage = (eventData: EventData) => void;
|
|||
/**
|
||||
* Describes the error handler signature.
|
||||
*/
|
||||
export type OnError = (error: EventHubsError | Error) => void;
|
||||
export type OnError = (error: MessagingError | Error) => void;
|
||||
|
||||
/**
|
||||
* Describes the EventHubReceiver that will receive event data from EventHub.
|
||||
|
@ -83,10 +82,10 @@ export class EventHubReceiver extends ClientEntity {
|
|||
*/
|
||||
receiverRuntimeMetricEnabled: boolean = false;
|
||||
/**
|
||||
* @property {any} [_receiver] The AMQP receiver link.
|
||||
* @property {Receiver} [_receiver] The AMQP receiver link.
|
||||
* @protected
|
||||
*/
|
||||
protected _receiver?: any;
|
||||
protected _receiver?: Receiver;
|
||||
/**
|
||||
* @property {OnMessage} _onMessage The message handler provided by the user that will be wrapped
|
||||
* inside _onAmqpMessage.
|
||||
|
@ -104,13 +103,13 @@ export class EventHubReceiver extends ClientEntity {
|
|||
* underlying rhea receiver for the "message" event.
|
||||
* @protected
|
||||
*/
|
||||
protected _onAmqpMessage: rheaPromise.OnAmqpEvent;
|
||||
protected _onAmqpMessage: OnAmqpEvent;
|
||||
/**
|
||||
* @property {OnMessage} _onMessage The message handler that will be set as the handler on the
|
||||
* underlying rhea receiver for the "receiver_error" event.
|
||||
* @protected
|
||||
*/
|
||||
protected _onAmqpError: rheaPromise.OnAmqpEvent;
|
||||
protected _onAmqpError: OnAmqpEvent;
|
||||
|
||||
/**
|
||||
* Instantiate a new receiver from the AMQP `Receiver`. Used by `EventHubClient`.
|
||||
|
@ -118,18 +117,7 @@ export class EventHubReceiver extends ClientEntity {
|
|||
* @constructor
|
||||
* @param {EventHubClient} client The EventHub client.
|
||||
* @param {string} partitionId Partition ID from which to receive.
|
||||
* @param {ReceiveOptions} [options] Options for how you'd like to connect.
|
||||
* @param {string} [options.consumerGroup] Consumer group from which to receive.
|
||||
* @param {number} [options.prefetchCount] The upper limit of events this receiver will
|
||||
* actively receive regardless of whether a receive operation is pending.
|
||||
* @param {boolean} [options.enableReceiverRuntimeMetric] Provides the approximate receiver runtime information
|
||||
* for a logical partition of an Event Hub if the value is true. Default false.
|
||||
* @param {number} [options.epoch] The epoch value that this receiver is currently
|
||||
* using for partition ownership. A value of undefined means this receiver is not an epoch-based receiver.
|
||||
* @param {EventPosition} [options.eventPosition] The position of EventData in the EventHub parition from
|
||||
* where the receiver should start receiving. Only one of offset, sequenceNumber, enqueuedTime, customFilter can be specified.
|
||||
* `EventPosition.withCustomFilter()` should be used if you want more fine-grained control of the filtering.
|
||||
* See https://github.com/Azure/amqpnetlite/wiki/Azure%20Service%20Bus%20Event%20Hubs for details.
|
||||
* @param {ReceiveOptions} [options] Receiver options.
|
||||
*/
|
||||
constructor(context: ConnectionContext, partitionId: string | number, options?: ReceiveOptions) {
|
||||
super(context, { partitionId: partitionId, name: options ? options.name : undefined });
|
||||
|
@ -145,7 +133,7 @@ export class EventHubReceiver extends ClientEntity {
|
|||
this.runtimeInfo = {
|
||||
partitionId: `${partitionId}`
|
||||
};
|
||||
this._onAmqpMessage = (context: rheaPromise.EventContext) => {
|
||||
this._onAmqpMessage = (context: EventContext) => {
|
||||
const evData = EventData.fromAmqpMessage(context.message!);
|
||||
evData.body = this._context.dataTransformer.decode(context.message!.body);
|
||||
|
||||
|
@ -158,7 +146,7 @@ export class EventHubReceiver extends ClientEntity {
|
|||
this._onMessage!(evData);
|
||||
};
|
||||
|
||||
this._onAmqpError = (context: rheaPromise.EventContext) => {
|
||||
this._onAmqpError = (context: EventContext) => {
|
||||
const ehError = translate(context.receiver!.error!);
|
||||
// TODO: Should we retry before calling user's error method?
|
||||
debug("[%s] An error occurred for Receiver '%s': %O.",
|
||||
|
@ -167,7 +155,13 @@ export class EventHubReceiver extends ClientEntity {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the AMQP receiver link is open. If open then returns true else returns false.
|
||||
* @return {boolean} boolean
|
||||
*/
|
||||
isOpen(): boolean {
|
||||
return this._receiver! && this._receiver!.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying AMQP receiver.
|
||||
|
@ -175,14 +169,10 @@ export class EventHubReceiver extends ClientEntity {
|
|||
async close(): Promise<void> {
|
||||
if (this._receiver) {
|
||||
try {
|
||||
// TODO: should I call _receiver.detach() or _receiver.close()?
|
||||
// should I also call this._session.close() after closing the reciver
|
||||
// or can I directly close the session which will take care of closing the receiver as well.
|
||||
await rheaPromise.closeReceiver(this._receiver);
|
||||
await this._receiver.close();
|
||||
// Resetting the mode.
|
||||
debug("[%s] Deleted the receiver '%s' from the client cache.", this._context.connectionId, this.name);
|
||||
this._receiver = undefined;
|
||||
this._session = undefined;
|
||||
clearTimeout(this._tokenRenewalTimer as NodeJS.Timer);
|
||||
debug("[%s] Receiver '%s', has been closed.", this._context.connectionId, this.name);
|
||||
} catch (err) {
|
||||
|
@ -195,9 +185,9 @@ export class EventHubReceiver extends ClientEntity {
|
|||
* Creates a new AMQP receiver under a new AMQP session.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
protected async _init(onAmqpMessage?: rheaPromise.OnAmqpEvent, onAmqpError?: rheaPromise.OnAmqpEvent): Promise<void> {
|
||||
protected async _init(onAmqpMessage?: OnAmqpEvent, onAmqpError?: OnAmqpEvent): Promise<void> {
|
||||
try {
|
||||
if (!this._isOpen()) {
|
||||
if (!this.isOpen()) {
|
||||
await this._negotiateClaim();
|
||||
if (!onAmqpMessage) {
|
||||
onAmqpMessage = this._onAmqpMessage;
|
||||
|
@ -205,10 +195,9 @@ export class EventHubReceiver extends ClientEntity {
|
|||
if (!onAmqpError) {
|
||||
onAmqpError = this._onAmqpError;
|
||||
}
|
||||
this._session = await rheaPromise.createSession(this._context.connection);
|
||||
debug("[%s] Trying to create receiver '%s'...", this._context.connectionId, this.name);
|
||||
const rcvrOptions = this._createReceiverOptions();
|
||||
this._receiver = await rheaPromise.createReceiverWithHandlers(this._session, onAmqpMessage, onAmqpError, rcvrOptions);
|
||||
const rcvrOptions = this._createReceiverOptions(onAmqpMessage, onAmqpError);
|
||||
this._receiver = await this._context.connection!.createReceiver(rcvrOptions);
|
||||
debug("Promise to create the receiver resolved. Created receiver with name: ", this.name);
|
||||
debug("[%s] Receiver '%s' created with receiver options: %O",
|
||||
this._context.connectionId, this.name, rcvrOptions);
|
||||
|
@ -225,34 +214,20 @@ export class EventHubReceiver extends ClientEntity {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the AMQP receiver link is open. If open then returns true else returns false.
|
||||
* @protected
|
||||
*
|
||||
* @return {boolean} boolean
|
||||
*/
|
||||
protected _isOpen(): boolean {
|
||||
let result: boolean = false;
|
||||
if (this._session && this._receiver) {
|
||||
if (this._receiver.is_open && this._receiver.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the options that need to be specified while creating an AMQP receiver link.
|
||||
* @private
|
||||
*/
|
||||
private _createReceiverOptions(): rheaPromise.ReceiverOptions {
|
||||
const rcvrOptions: rheaPromise.ReceiverOptions = {
|
||||
private _createReceiverOptions(onMessage?: OnAmqpEvent, onError?: OnAmqpEvent): ReceiverOptions {
|
||||
const rcvrOptions: ReceiverOptions = {
|
||||
name: this.name,
|
||||
autoaccept: true,
|
||||
source: {
|
||||
address: this.address
|
||||
},
|
||||
credit_window: this.prefetchCount,
|
||||
onMessage: onMessage,
|
||||
onError: onError
|
||||
};
|
||||
if (this.epoch !== undefined && this.epoch !== null) {
|
||||
if (!rcvrOptions.properties) rcvrOptions.properties = {};
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import * as uuid from "uuid/v4";
|
||||
import { translate } from "./errors";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import { EventData, messageProperties } from "./eventData";
|
||||
import {
|
||||
messageProperties, Sender, EventContext, OnAmqpEvent, SenderOptions, Delivery, SenderEvents,
|
||||
message
|
||||
} from "./rhea-promise";
|
||||
import { EventData } from "./eventData";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { defaultLock, Func } from "./util/utils";
|
||||
import { retry } from "./retry";
|
||||
import { defaultLock, Func, retry, translate, AmqpMessage } from "./amqp-common";
|
||||
import { ClientEntity } from "./clientEntity";
|
||||
|
||||
const debug = debugModule("azure:event-hubs:sender");
|
||||
|
@ -26,10 +26,10 @@ export class EventHubSender extends ClientEntity {
|
|||
*/
|
||||
readonly senderLock: string = `sender-${uuid()}`;
|
||||
/**
|
||||
* @property {any} [_sender] The AMQP sender link.
|
||||
* @property {Sender} [_sender] The AMQP sender link.
|
||||
* @private
|
||||
*/
|
||||
private _sender?: any;
|
||||
private _sender?: Sender;
|
||||
|
||||
/**
|
||||
* Creates a new EventHubSender instance.
|
||||
|
@ -51,9 +51,9 @@ export class EventHubSender extends ClientEntity {
|
|||
* Sends the given message, with the given options on this link
|
||||
*
|
||||
* @param {any} data Message to send. Will be sent as UTF8-encoded JSON string.
|
||||
* @returns {Promise<rheaPromise.Delivery>} Promise<rheaPromise.Delivery>
|
||||
* @returns {Promise<Delivery>} Promise<Delivery>
|
||||
*/
|
||||
async send(data: EventData): Promise<rheaPromise.Delivery> {
|
||||
async send(data: EventData): Promise<Delivery> {
|
||||
try {
|
||||
if (!data || (data && typeof data !== "object")) {
|
||||
throw new Error("data is required and it must be of type object.");
|
||||
|
@ -78,9 +78,9 @@ export class EventHubSender extends ClientEntity {
|
|||
* "application_properties" and "properties" of the first message will be set as that
|
||||
* of the envelope (batch message).
|
||||
* @param {Array<EventData>} datas An array of EventData objects to be sent in a Batch message.
|
||||
* @return {Promise<rheaPromise.Delivery>} Promise<rheaPromise.Delivery>
|
||||
* @return {Promise<Delivery>} Promise<Delivery>
|
||||
*/
|
||||
async sendBatch(datas: EventData[]): Promise<rheaPromise.Delivery> {
|
||||
async sendBatch(datas: EventData[]): Promise<Delivery> {
|
||||
try {
|
||||
if (!datas || (datas && !Array.isArray(datas))) {
|
||||
throw new Error("data is required and it must be an Array.");
|
||||
|
@ -93,7 +93,7 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
debug("[%s] Sender '%s', trying to send EventData[]: %O",
|
||||
this._context.connectionId, this.name, datas);
|
||||
const messages: rhea.Message[] = [];
|
||||
const messages: AmqpMessage[] = [];
|
||||
// Convert EventData to AmqpMessage.
|
||||
for (let i = 0; i < datas.length; i++) {
|
||||
const message = EventData.toAmqpMessage(datas[i]);
|
||||
|
@ -101,8 +101,8 @@ export class EventHubSender extends ClientEntity {
|
|||
messages[i] = message;
|
||||
}
|
||||
// Encode every amqp message and then convert every encoded message to amqp data section
|
||||
const batchMessage: rhea.Message = {
|
||||
body: rhea.message.data_sections(messages.map(rhea.message.encode))
|
||||
const batchMessage: AmqpMessage = {
|
||||
body: message.data_sections(messages.map(message.encode))
|
||||
};
|
||||
// Set message_annotations, application_properties and properties of the first message as
|
||||
// that of the envelope (batch message).
|
||||
|
@ -119,7 +119,7 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
|
||||
// Finally encode the envelope (batch message).
|
||||
const encodedBatchMessage = rhea.message.encode(batchMessage);
|
||||
const encodedBatchMessage = message.encode(batchMessage);
|
||||
debug("[%s]Sender '%s', sending encoded batch message.",
|
||||
this._context.connectionId, this.name, encodedBatchMessage);
|
||||
return await this._trySend(encodedBatchMessage, undefined, 0x80013700);
|
||||
|
@ -137,12 +137,11 @@ export class EventHubSender extends ClientEntity {
|
|||
async close(): Promise<void> {
|
||||
if (this._sender) {
|
||||
try {
|
||||
await rheaPromise.closeSender(this._sender);
|
||||
await this._sender.close();
|
||||
delete this._context.senders[this.name!];
|
||||
debug("[%s] Deleted the sender '%s' with address '%s' from the client cache.",
|
||||
this._context.connectionId, this.name, this.address);
|
||||
this._sender = undefined;
|
||||
this._session = undefined;
|
||||
clearTimeout(this._tokenRenewalTimer as NodeJS.Timer);
|
||||
debug("[%s]Sender '%s' closed.", this._context.connectionId, this.name);
|
||||
} catch (err) {
|
||||
|
@ -152,12 +151,13 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
}
|
||||
|
||||
private _createSenderOptions(): rheaPromise.SenderOptions {
|
||||
const options: rheaPromise.SenderOptions = {
|
||||
private _createSenderOptions(onError?: OnAmqpEvent): SenderOptions {
|
||||
const options: SenderOptions = {
|
||||
name: this.name,
|
||||
target: {
|
||||
address: this.address
|
||||
}
|
||||
},
|
||||
onError: onError
|
||||
};
|
||||
debug("Creating sender with options: %O", options);
|
||||
return options;
|
||||
|
@ -171,26 +171,26 @@ export class EventHubSender extends ClientEntity {
|
|||
* for the message to be accepted or rejected and accordingly resolve or reject the promise.
|
||||
*
|
||||
* @param message The message to be sent to EventHub.
|
||||
* @return {Promise<rheaPromise.Delivery>} Promise<rheaPromise.Delivery>
|
||||
* @return {Promise<Delivery>} Promise<Delivery>
|
||||
*/
|
||||
private _trySend(message: rhea.Message, tag?: any, format?: number): Promise<rheaPromise.Delivery> {
|
||||
const sendEventPromise = new Promise<rheaPromise.Delivery>((resolve, reject) => {
|
||||
private _trySend(message: AmqpMessage, tag?: any, format?: number): Promise<Delivery> {
|
||||
const sendEventPromise = new Promise<Delivery>((resolve, reject) => {
|
||||
debug("[%s] Sender '%s', credit: %d available: %d", this._context.connectionId, this.name,
|
||||
this._sender.credit, this._sender.session.outgoing.available());
|
||||
if (this._sender.sendable()) {
|
||||
this._sender!.credit, this._sender!.session.outgoing.available());
|
||||
if (this._sender!.sendable()) {
|
||||
debug("[%s] Sender '%s', sending message: %O", this._context.connectionId, this.name, message);
|
||||
let onRejected: Func<rheaPromise.EventContext, void>;
|
||||
let onReleased: Func<rheaPromise.EventContext, void>;
|
||||
let onModified: Func<rheaPromise.EventContext, void>;
|
||||
let onAccepted: Func<rheaPromise.EventContext, void>;
|
||||
let onRejected: Func<EventContext, void>;
|
||||
let onReleased: Func<EventContext, void>;
|
||||
let onModified: Func<EventContext, void>;
|
||||
let onAccepted: Func<EventContext, void>;
|
||||
const removeListeners = (): void => {
|
||||
this._sender.removeListener("rejected", onRejected);
|
||||
this._sender.removeListener("accepted", onAccepted);
|
||||
this._sender.removeListener("released", onReleased);
|
||||
this._sender.removeListener("modified", onModified);
|
||||
this._sender!.removeHandler(SenderEvents.rejected, onRejected);
|
||||
this._sender!.removeHandler(SenderEvents.accepted, onAccepted);
|
||||
this._sender!.removeHandler(SenderEvents.released, onReleased);
|
||||
this._sender!.removeHandler(SenderEvents.modified, onModified);
|
||||
};
|
||||
|
||||
onAccepted = (context: rheaPromise.EventContext) => {
|
||||
onAccepted = (context: EventContext) => {
|
||||
// Since we will be adding listener for accepted and rejected event every time
|
||||
// we send a message, we need to remove listener for both the events.
|
||||
// This will ensure duplicate listeners are not added for the same event.
|
||||
|
@ -198,12 +198,12 @@ export class EventHubSender extends ClientEntity {
|
|||
debug("[%s] Sender '%s', got event accepted.", this._context.connectionId, this.name);
|
||||
resolve(context.delivery);
|
||||
};
|
||||
onRejected = (context: rheaPromise.EventContext) => {
|
||||
onRejected = (context: EventContext) => {
|
||||
removeListeners();
|
||||
debug("[%s] Sender '%s', got event rejected.", this._context.connectionId, this.name);
|
||||
reject(translate(context!.delivery!.remote_state!.error));
|
||||
};
|
||||
onReleased = (context: rheaPromise.EventContext) => {
|
||||
onReleased = (context: EventContext) => {
|
||||
removeListeners();
|
||||
debug("[%s] Sender '%s', got event released.", this._context.connectionId, this.name);
|
||||
let err: Error;
|
||||
|
@ -215,7 +215,7 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
reject(err);
|
||||
};
|
||||
onModified = (context: rheaPromise.EventContext) => {
|
||||
onModified = (context: EventContext) => {
|
||||
removeListeners();
|
||||
debug("[%s] Sender '%s', got event modified.", this._context.connectionId, this.name);
|
||||
let err: Error;
|
||||
|
@ -227,11 +227,11 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
reject(err);
|
||||
};
|
||||
this._sender.on("accepted", onAccepted);
|
||||
this._sender.on("rejected", onRejected);
|
||||
this._sender.on("modified", onModified);
|
||||
this._sender.on("released", onReleased);
|
||||
const delivery = this._sender.send(message, tag, format);
|
||||
this._sender!.registerHandler(SenderEvents.accepted, onAccepted);
|
||||
this._sender!.registerHandler(SenderEvents.rejected, onRejected);
|
||||
this._sender!.registerHandler(SenderEvents.modified, onModified);
|
||||
this._sender!.registerHandler(SenderEvents.released, onReleased);
|
||||
const delivery = this._sender!.send(message, tag, format);
|
||||
debug("[%s] Sender '%s', sent message with delivery id: %d",
|
||||
this._context.connectionId, this.name, delivery.id);
|
||||
} else {
|
||||
|
@ -242,7 +242,7 @@ export class EventHubSender extends ClientEntity {
|
|||
}
|
||||
});
|
||||
|
||||
return retry<rheaPromise.Delivery>(() => sendEventPromise);
|
||||
return retry<Delivery>(() => sendEventPromise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,13 +252,7 @@ export class EventHubSender extends ClientEntity {
|
|||
* @return {boolean} boolean
|
||||
*/
|
||||
private _isOpen(): boolean {
|
||||
let result: boolean = false;
|
||||
if (this._session && this._sender) {
|
||||
if (this._sender.is_open && this._sender.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return this._sender! && this._sender!.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,16 +263,15 @@ export class EventHubSender extends ClientEntity {
|
|||
try {
|
||||
if (!this._isOpen()) {
|
||||
await this._negotiateClaim();
|
||||
const onAmqpError = (context: rheaPromise.EventContext) => {
|
||||
const onAmqpError: OnAmqpEvent = (context: EventContext) => {
|
||||
const senderError = translate(context.sender!.error!);
|
||||
// TODO: Should we retry before calling user's error method?
|
||||
debug("[%s] An error occurred for sender '%s': %O.",
|
||||
this._context.connectionId, this.name, senderError);
|
||||
};
|
||||
this._session = await rheaPromise.createSession(this._context.connection);
|
||||
debug("[%s] Trying to create sender '%s'...", this._context.connectionId, this.name);
|
||||
const options = this._createSenderOptions();
|
||||
this._sender = await rheaPromise.createSenderWithHandlers(this._session, onAmqpError, options);
|
||||
const options = this._createSenderOptions(onAmqpError);
|
||||
this._sender = await this._context.connection!.createSender(options);
|
||||
debug("[%s] Promise to create the sender resolved. Created sender with name: %s",
|
||||
this._context.connectionId, this.name);
|
||||
debug("[%s] Sender '%s' created with sender options: %O",
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as Constants from "./util/constants";
|
||||
import { translate } from "./errors";
|
||||
import { ErrorNameConditionMapper } from ".";
|
||||
import { translate, Constants, ErrorNameConditionMapper } from "./amqp-common";
|
||||
|
||||
/**
|
||||
* Describes the options that can be set while creating an EventPosition.
|
||||
|
@ -109,7 +107,10 @@ export class EventPosition {
|
|||
}
|
||||
|
||||
if (!result) {
|
||||
throw translate({ condition: ErrorNameConditionMapper.ArgumentError, description: "No starting position was set in the EventPosition." });
|
||||
throw translate({
|
||||
condition: ErrorNameConditionMapper.ArgumentError,
|
||||
description: "No starting position was set in the EventPosition."
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -3,22 +3,21 @@
|
|||
|
||||
export { EventData } from "./eventData";
|
||||
export {
|
||||
Delivery, AmqpError, Message, MessageHeader, MessageProperties, Dictionary,
|
||||
EventHubDeliveryAnnotations, EventHubMessageAnnotations
|
||||
Delivery, AmqpError, Message, MessageHeader, MessageProperties, Dictionary
|
||||
} from "./rhea-promise";
|
||||
export { ConnectionConfig } from "./connectionConfig";
|
||||
export { ReceiverRuntimeInfo, OnMessage, OnError } from "./eventHubReceiver";
|
||||
export { ReceiveHandler } from "./streamingReceiver";
|
||||
export {
|
||||
EventHubsError, ErrorNameConditionMapper, ConditionStatusMapper, ConditionErrorNameMapper
|
||||
} from "./errors";
|
||||
export { EventHubClient, ReceiveOptions, ClientOptionsBase, ClientOptions } from "./eventHubClient";
|
||||
export { EventPosition } from "./eventPosition";
|
||||
export { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
|
||||
export { EventHubPartitionRuntimeInformation, EventHubRuntimeInformation } from "./managementClient";
|
||||
export { TokenType, TokenProvider, TokenInfo } from "./auth/token";
|
||||
export { aadEventHubsAudience } from "./util/constants";
|
||||
export {
|
||||
TokenType, TokenProvider, TokenInfo, EventHubDeliveryAnnotations, EventHubMessageAnnotations
|
||||
} from "./amqp-common";
|
||||
import { Constants } from "./amqp-common";
|
||||
export const aadEventHubsAudience = Constants.aadEventHubsAudience;
|
||||
export {
|
||||
delay, Timeout, EventHubConnectionStringModel, parseConnectionString,
|
||||
IotHubConnectionStringModel, StorageConnectionStringModel, isIotHubConnectionString
|
||||
} from "./util/utils";
|
||||
IotHubConnectionStringModel, StorageConnectionStringModel, isIotHubConnectionString,
|
||||
ErrorNameConditionMapper, ConditionStatusMapper, ConditionErrorNameMapper, MessagingError,
|
||||
DataTransformer, DefaultDataTransformer, ConnectionConfig
|
||||
} from "./amqp-common";
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { parseConnectionString, IotHubConnectionStringModel } from "../util/utils";
|
||||
import { ConnectionConfig } from "../connectionConfig";
|
||||
import { parseConnectionString, IotHubConnectionStringModel, ConnectionConfig } from "../amqp-common";
|
||||
|
||||
export interface IotHubConnectionConfig {
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import { SasTokenProvider } from "../auth/sas";
|
||||
import { SasTokenProvider } from "../amqp-common";
|
||||
import { TokenInfo } from "..";
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,8 +5,8 @@ import { IotHubConnectionConfig } from "./iotHubConnectionConfig";
|
|||
import { ConnectionContext, ConnectionContextOptions } from "../connectionContext";
|
||||
import { IotSasTokenProvider } from "./iotSas";
|
||||
import * as debugModule from "debug";
|
||||
import { translate, EventHubsError } from "../errors";
|
||||
import { closeConnection } from "../rhea-promise";
|
||||
import { translate, MessagingError } from "../amqp-common";
|
||||
import { } from "../rhea-promise";
|
||||
const debug = debugModule("azure:event-hubs:iothubClient");
|
||||
|
||||
export interface ParsedRedirectError {
|
||||
|
@ -78,7 +78,7 @@ export class IotHubClient {
|
|||
// Close the management session
|
||||
await context.managementSession!.close();
|
||||
debug("IotHub management client closed.");
|
||||
await closeConnection(context.connection);
|
||||
await context.connection!.close();
|
||||
debug("Closed the amqp connection '%s' on the iothub client.", context.connectionId);
|
||||
context.connection = undefined;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export class IotHubClient {
|
|||
}
|
||||
}
|
||||
|
||||
private _parseRedirectError(error: EventHubsError): ParsedRedirectError {
|
||||
private _parseRedirectError(error: MessagingError): ParsedRedirectError {
|
||||
if (!error) {
|
||||
throw new Error("'error' is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as uuid from "uuid/v4";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import * as Constants from "./util/constants";
|
||||
import * as debugModule from "debug";
|
||||
import { RequestResponseLink, createRequestResponseLink, sendRequest } from "./rpc";
|
||||
import { defaultLock } from "./util/utils";
|
||||
import * as rheaPromise from "./rhea-promise";
|
||||
import {
|
||||
RequestResponseLink, defaultLock, translate, Constants
|
||||
} from "./amqp-common";
|
||||
import { Message } from ".";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { ClientEntity } from "./clientEntity";
|
||||
import { translate } from "./errors";
|
||||
|
||||
const debug = debugModule("azure:event-hubs:management");
|
||||
|
||||
|
@ -170,10 +169,9 @@ export class ManagementClient extends ClientEntity {
|
|||
*/
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
if (this._mgmtReqResLink) {
|
||||
await rheaPromise.closeSession(this._mgmtReqResLink.session);
|
||||
if (this._isMgmtRequestResponseLinkOpen()) {
|
||||
await this._mgmtReqResLink!.close();
|
||||
debug("Successfully closed the management session.");
|
||||
this._session = undefined;
|
||||
this._mgmtReqResLink = undefined;
|
||||
clearTimeout(this._tokenRenewalTimer as NodeJS.Timer);
|
||||
}
|
||||
|
@ -185,7 +183,7 @@ export class ManagementClient extends ClientEntity {
|
|||
}
|
||||
|
||||
private async _init(): Promise<void> {
|
||||
if (!this._mgmtReqResLink) {
|
||||
if (!this._isMgmtRequestResponseLinkOpen()) {
|
||||
await this._negotiateClaim();
|
||||
const rxopt: rheaPromise.ReceiverOptions = {
|
||||
source: { address: this.address },
|
||||
|
@ -194,7 +192,8 @@ export class ManagementClient extends ClientEntity {
|
|||
};
|
||||
const sropt: rheaPromise.SenderOptions = { target: { address: this.address } };
|
||||
debug("Creating a session for $management endpoint");
|
||||
this._mgmtReqResLink = await createRequestResponseLink(this._context.connection, sropt, rxopt);
|
||||
this._mgmtReqResLink =
|
||||
await RequestResponseLink.create(this._context.connection!, sropt, rxopt);
|
||||
this._session = this._mgmtReqResLink.session;
|
||||
debug("[%s] Created sender '%s' and receiver '%s' links for $management endpoint.",
|
||||
this._context.connectionId, this._mgmtReqResLink.sender.name, this._mgmtReqResLink.receiver.name);
|
||||
|
@ -227,12 +226,17 @@ export class ManagementClient extends ClientEntity {
|
|||
if (partitionId && type === Constants.partition) {
|
||||
request.application_properties!.partition = partitionId;
|
||||
}
|
||||
debug("[%s] Acquiring lock to get the management req res link.", this._context.connectionId);
|
||||
await defaultLock.acquire(this.managementLock, () => { return this._init(); });
|
||||
return sendRequest(this._context.connection, this._mgmtReqResLink!, request);
|
||||
return await this._mgmtReqResLink!.sendRequest(request);
|
||||
} catch (err) {
|
||||
err = translate(err);
|
||||
debug("An error occurred while making the request to $management endpoint: %O", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private _isMgmtRequestResponseLinkOpen(): boolean {
|
||||
return this._mgmtReqResLink! && this._mgmtReqResLink!.isOpen();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import { Session } from "./session";
|
||||
import { Sender, SenderOptions } from "./sender";
|
||||
import { Receiver, ReceiverOptions } from "./receiver";
|
||||
import { Func, ConnectionEvents } from ".";
|
||||
|
||||
const debug = debugModule("rhea-promise:connection");
|
||||
|
||||
export interface SenderOptionsWithSession extends SenderOptions {
|
||||
session?: Session;
|
||||
}
|
||||
|
||||
export interface ReceiverOptionsWithSession extends ReceiverOptions {
|
||||
session?: Session;
|
||||
}
|
||||
|
||||
export interface ReqResLink {
|
||||
sender: Sender;
|
||||
receiver: Receiver;
|
||||
session: Session;
|
||||
}
|
||||
|
||||
export class Connection {
|
||||
options?: rhea.ConnectionOptions;
|
||||
private _connection: rhea.Connection;
|
||||
|
||||
constructor(options?: rhea.ConnectionOptions) {
|
||||
this.options = options;
|
||||
this._connection = rhea.connect(options);
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._connection.options.id!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new amqp connection.
|
||||
* @param {ConnectionOptions} [options] Options to be provided for establishing an amqp connection.
|
||||
* @return {Promise<Connection>} Promise<Connection>
|
||||
* - **Resolves** the promise with the Connection object when rhea emits the "connection_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "connection_close" event while trying
|
||||
* to establish an amqp connection.
|
||||
*/
|
||||
open(): Promise<Connection> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isOpen()) {
|
||||
|
||||
let onOpen: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
let onTransportClose: Func<rhea.EventContext, void>;
|
||||
|
||||
const removeListeners: Function = () => {
|
||||
this._connection!.removeListener("connection_open", onOpen);
|
||||
this._connection!.removeListener("connection_close", onClose);
|
||||
this._connection!.removeListener("disconnected", onClose);
|
||||
this._connection!.removeListener("disconnected", onTransportClose);
|
||||
};
|
||||
|
||||
onOpen = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
this._connection!.once("disconnected", onTransportClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise with amqp connection.");
|
||||
resolve(this);
|
||||
});
|
||||
};
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
debug(`Error occurred while establishing amqp connection.`, context.connection.error);
|
||||
reject(context.connection.error);
|
||||
};
|
||||
|
||||
onTransportClose = (context: rhea.EventContext) => {
|
||||
debug(`Error occurred on the amqp connection.`, context.connection.error);
|
||||
this.close().then(() => {
|
||||
context.connection = undefined as any;
|
||||
}).catch((err: Error) => {
|
||||
debug(`Error occurred while closing amqp connection.`, err);
|
||||
});
|
||||
};
|
||||
|
||||
this._connection!.once("connection_open", onOpen);
|
||||
this._connection!.once("connection_close", onClose);
|
||||
this._connection!.once("disconnected", onClose);
|
||||
} else {
|
||||
resolve(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closes the amqp connection.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "connection_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "connection_error" event while trying
|
||||
* to close an amqp connection.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (this._connection && this._connection.is_open()) {
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
let onError: Func<rhea.EventContext, void>;
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
this._connection!.removeListener("connection_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the connection has been successfully closed.");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
onError = (context: rhea.EventContext) => {
|
||||
this._connection!.removeListener("connection_error", onError);
|
||||
debug(`Error occurred while closing amqp connection.`, context.connection.error);
|
||||
reject(context.connection.error);
|
||||
};
|
||||
|
||||
this._connection.once("connection_close", onClose);
|
||||
this._connection.once("connection_error", onError);
|
||||
this._connection.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the connection is open.
|
||||
* @return {boolean} true if open false otherwise.
|
||||
*/
|
||||
isOpen(): boolean {
|
||||
let result: boolean = false;
|
||||
if (this._connection && this._connection.is_open && this._connection.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp session on the provided amqp connection.
|
||||
* @return {Promise<Session>} Promise<Session>
|
||||
* - **Resolves** the promise with the Session object when rhea emits the "session_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "session_close" event while trying
|
||||
* to create an amqp session.
|
||||
*/
|
||||
createSession(): Promise<Session> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rheaSession = this._connection.create_session();
|
||||
const session = new Session(this, rheaSession);
|
||||
let onOpen: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
|
||||
const removeListeners = () => {
|
||||
rheaSession.removeListener("session_open", onOpen);
|
||||
rheaSession.removeListener("session_close", onClose);
|
||||
};
|
||||
|
||||
onOpen = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise with amqp session.");
|
||||
resolve(session);
|
||||
});
|
||||
};
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
debug(`Error occurred while establishing a session over amqp connection.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
};
|
||||
|
||||
rheaSession.once("session_open", onOpen);
|
||||
rheaSession.once("session_close", onClose);
|
||||
debug("Calling amqp session.begin().");
|
||||
rheaSession.begin();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp sender link. It either uses the provided session or creates a new one.
|
||||
* @param {SenderOptionsWithSession} options Optional parameters to create a sender link.
|
||||
* @return {Promise<Sender>} Promise<Sender>.
|
||||
*/
|
||||
async createSender(options?: SenderOptionsWithSession): Promise<Sender> {
|
||||
if (options && options.session) {
|
||||
return await options.session.createSender(options);
|
||||
}
|
||||
const session = await this.createSession();
|
||||
return await session.createSender(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp receiver link. It either uses the provided session or creates a new one.
|
||||
* @param {ReceiverOptionsWithSession} options Optional parameters to create a receiver link.
|
||||
* @return {Promise<Receiver>} Promise<Receiver>.
|
||||
*/
|
||||
async createReceiver(options?: ReceiverOptionsWithSession): Promise<Receiver> {
|
||||
if (options && options.session) {
|
||||
return await options.session.createReceiver(options);
|
||||
}
|
||||
const session = await this.createSession();
|
||||
return await session.createReceiver(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp sender-receiver link. It either uses the provided session or creates a new one.
|
||||
* This method creates a sender-receiver link on the same session. It is useful for management
|
||||
* style operations where one may want to send a request and await for response.
|
||||
* @param {SenderOptions} senderOptions Parameters to create a sender.
|
||||
* @param {ReceiverOptions} receiverOptions Parameters to create a receiver.
|
||||
* @param {Session} [session] The optional session on which the sender and receiver links will be
|
||||
* created.
|
||||
* @return {Promise<ReqResLink>} Promise<ReqResLink>
|
||||
*/
|
||||
async createRequestResponseLink(senderOptions: SenderOptions, receiverOptions: ReceiverOptions, providedSession?: Session): Promise<ReqResLink> {
|
||||
if (!senderOptions) {
|
||||
throw new Error(`Please provide sender options.`);
|
||||
}
|
||||
if (!receiverOptions) {
|
||||
throw new Error(`Please provide receiver options.`);
|
||||
}
|
||||
const session = providedSession || await this.createSession();
|
||||
const sender = await session.createSender(senderOptions);
|
||||
const receiver = await session.createReceiver(receiverOptions);
|
||||
debug("[%s] Successfully created the sender and receiver links on the same session.", this.id);
|
||||
return {
|
||||
session: session,
|
||||
sender: sender,
|
||||
receiver: receiver
|
||||
};
|
||||
}
|
||||
|
||||
registerHandler(event: ConnectionEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._connection.on(event, handler);
|
||||
}
|
||||
|
||||
removeHandler(event: ConnectionEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._connection.removeListener(event, handler);
|
||||
}
|
||||
}
|
|
@ -1,443 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
|
||||
const debug = debugModule("rhea-promise");
|
||||
|
||||
export {
|
||||
Delivery, Message, OnAmqpEvent, MessageProperties, MessageHeader, EventContext,
|
||||
Connection, ReceiverOptions, SenderOptions, ConnectionOptions, AmqpError, Dictionary
|
||||
ConnectionOptions, AmqpError, Dictionary, types, message, filter, Filter, MessageUtil,
|
||||
uuid_to_string, generate_uuid, string_to_uuid, LinkError, ProtocolError, LinkOptions,
|
||||
DeliveryAnnotations, MessageAnnotations, ReceiverEvents, SenderEvents, ConnectionEvents,
|
||||
SessionEvents
|
||||
} from "rhea";
|
||||
|
||||
/**
|
||||
* Establishes an amqp connection.
|
||||
* @param {ConnectionOptions} [options] Options to be provided for establishing an amqp connection.
|
||||
* @return {Promise<Connection>} Promise<Connection>
|
||||
* - **Resolves** the promise with the Connection object when rhea emits the "connection_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "connection_close" event while trying
|
||||
* to establish an amqp connection.
|
||||
*/
|
||||
export function connect(options?: rhea.ConnectionOptions): Promise<rhea.Connection> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const connection = rhea.connect(options);
|
||||
|
||||
function removeListeners(connection: rhea.Connection): void {
|
||||
connection.removeListener("connection_open", onOpen);
|
||||
connection.removeListener("connection_close", onClose);
|
||||
connection.removeListener("disconnected", onClose);
|
||||
connection.removeListener("disconnected", onTransportClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(connection);
|
||||
connection.once("disconnected", onTransportClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise with amqp connection.");
|
||||
resolve(connection);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(connection);
|
||||
debug(`Error occurred while establishing amqp connection.`, context.connection.error);
|
||||
reject(context.connection.error);
|
||||
}
|
||||
|
||||
function onTransportClose(context: rhea.EventContext): void {
|
||||
debug(`Error occurred on the amqp connection.`, context.connection.error);
|
||||
closeConnection(context.connection).then(() => {
|
||||
context.connection = undefined as any;
|
||||
}).catch((err: Error) => {
|
||||
debug(`Error occurred while closing amqp connection.`, err);
|
||||
});
|
||||
}
|
||||
|
||||
connection.once("connection_open", onOpen);
|
||||
connection.once("connection_close", onClose);
|
||||
connection.once("disconnected", onClose);
|
||||
});
|
||||
}
|
||||
export { Connection, ReqResLink } from "./connection";
|
||||
export { Session } from "./session";
|
||||
export { Receiver, ReceiverOptions } from "./receiver";
|
||||
export { Sender, SenderOptions } from "./sender";
|
||||
|
||||
/**
|
||||
* Closes the amqp connection.
|
||||
* @param {Connection} connection The amqp connection that needs to be closed.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "connection_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "connection_error" event while trying
|
||||
* to close an amqp connection.
|
||||
*/
|
||||
export function closeConnection(connection: rhea.Connection): Promise<void> {
|
||||
if (!connection || (connection && typeof connection !== "object")) {
|
||||
throw new Error("connection is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (connection.is_open()) {
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
connection.removeListener("connection_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the connection has been successfully closed.");
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function onError(context: rhea.EventContext): void {
|
||||
connection.removeListener("connection_error", onError);
|
||||
debug(`Error occurred while closing amqp connection.`, context.connection.error);
|
||||
reject(context.connection.error);
|
||||
}
|
||||
|
||||
connection.once("connection_close", onClose);
|
||||
connection.once("connection_error", onError);
|
||||
connection.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp session on the provided amqp connection.
|
||||
* @param {Connection} connection The amqp connection object
|
||||
* @return {Promise<Session>} Promise<Session>
|
||||
* - **Resolves** the promise with the Session object when rhea emits the "session_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "session_close" event while trying
|
||||
* to create an amqp session.
|
||||
*/
|
||||
export function createSession(connection: rhea.Connection): Promise<rhea.Session> {
|
||||
if (!connection || (connection && typeof connection !== "object")) {
|
||||
throw new Error("connection is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const session = connection.create_session();
|
||||
|
||||
function removeListeners(session: rhea.Session): void {
|
||||
session.removeListener("session_open", onOpen);
|
||||
session.removeListener("session_close", onClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise with amqp session.");
|
||||
resolve(session);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
debug(`Error occurred while establishing a session over amqp connection.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
}
|
||||
|
||||
session.once("session_open", onOpen);
|
||||
session.once("session_close", onClose);
|
||||
debug("Calling amqp session.begin().");
|
||||
session.begin();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp session.
|
||||
* @param {Session} session The amqp session that needs to be closed.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "session_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "session_error" event while trying
|
||||
* to close an amqp session.
|
||||
*/
|
||||
export function closeSession(session: rhea.Session): Promise<void> {
|
||||
if (!session || (session && typeof session !== "object")) {
|
||||
throw new Error("session is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (session.is_open()) {
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
session.removeListener("session_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp session has been closed.");
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function onError(context: rhea.EventContext): void {
|
||||
session.removeListener("session_error", onError);
|
||||
debug(`Error occurred while closing amqp session.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
}
|
||||
|
||||
session.once("session_close", onClose);
|
||||
session.once("session_error", onError);
|
||||
session.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp sender on the provided amqp session.
|
||||
* @param {Session} session The amqp session object on which the sender link needs to be established.
|
||||
* @param {SenderOptions} [options] Options that can be provided while creating an amqp sender.
|
||||
* @return {Promise<Sender>} Promise<Sender>
|
||||
* - **Resolves** the promise with the Sender object when rhea emits the "sender_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "sender_close" event while trying
|
||||
* to create an amqp sender.
|
||||
*/
|
||||
export function createSender(session: rhea.Session, options?: rhea.SenderOptions): Promise<rhea.Sender> {
|
||||
if (!session || (session && typeof session !== "object")) {
|
||||
throw new Error("session is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const sender = session.attach_sender(options);
|
||||
|
||||
function removeListeners(session: rhea.Session): void {
|
||||
sender.removeListener("sendable", onOpen);
|
||||
sender.removeListener("sender_close", onClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp sender "${sender.name}".`);
|
||||
resolve(sender);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
debug(`Error occurred while creating a sender over amqp connection.`, context.sender!.error);
|
||||
reject(context.sender!.error);
|
||||
}
|
||||
|
||||
sender.once("sendable", onOpen);
|
||||
sender.once("sender_close", onClose);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp sender on the provided amqp session.
|
||||
* @param {Session} session The amqp session object on which the sender link needs to be established.
|
||||
* @param {OnAmqpEvent} onError The event handler for the "error" event for the sender.
|
||||
* @param {SenderOptions} [options] Options that can be provided while creating an amqp sender.
|
||||
* @return {Promise<Sender>} Promise<Sender>
|
||||
* - **Resolves** the promise with the Sender object when rhea emits the "sender_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "sender_close" event while trying
|
||||
* to create an amqp sender.
|
||||
*/
|
||||
export function createSenderWithHandlers(session: rhea.Session, onError: rhea.OnAmqpEvent, options?: rhea.SenderOptions): Promise<rhea.Sender> {
|
||||
if (!session || (session && typeof session !== "object")) {
|
||||
throw new Error("session is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const sender = session.attach_sender(options);
|
||||
sender.on("sender_error", onError);
|
||||
|
||||
function removeListeners(session: rhea.Session): void {
|
||||
sender.removeListener("sendable", onOpen);
|
||||
sender.removeListener("sender_close", onClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp sender "${sender.name}".`);
|
||||
resolve(sender);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(session);
|
||||
debug(`Error occurred while creating a sender over amqp connection.`, context.sender!.error);
|
||||
reject(context.sender!.error);
|
||||
}
|
||||
|
||||
sender.once("sendable", onOpen);
|
||||
sender.once("sender_close", onClose);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp sender.
|
||||
* @param {Sender} sender The amqp sender that needs to be closed.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "sender_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the
|
||||
* "sender_error" event while trying to close an amqp sender.
|
||||
*/
|
||||
export function closeSender(sender: rhea.Sender): Promise<void> {
|
||||
if (!sender || (sender && typeof sender !== "object")) {
|
||||
throw new Error("sender is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (sender.is_open()) {
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
sender.removeListener("sender_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp sender has been closed.");
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function onError(context: rhea.EventContext): void {
|
||||
sender.removeListener("sender_error", onError);
|
||||
debug(`Error occurred while closing amqp sender.`, context.sender!.error);
|
||||
reject(context.sender!.error);
|
||||
}
|
||||
|
||||
sender.once("sender_close", onClose);
|
||||
sender.once("sender_error", onError);
|
||||
sender.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp receiver on the provided amqp session. This method should be used when you will be
|
||||
* sending a request and waiting for a response from the service. For example: This method is useful
|
||||
* while creating request/response links for $management or $cbs endpoint.
|
||||
* @param {Session} session The amqp session object on which the receiver link needs to be established.
|
||||
* @param {ReceiverOptions} [options] Options that can be provided while creating an amqp receiver.
|
||||
* @return {Promise<Receiver>} Promise<Receiver>
|
||||
* - **Resolves** the promise with the Receiver object when rhea emits the "receiver_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "receiver_close" event while trying
|
||||
* to create an amqp receiver.
|
||||
*/
|
||||
export function createReceiver(session: rhea.Session, options?: rhea.ReceiverOptions): Promise<rhea.Receiver> {
|
||||
if (!session || (session && typeof session !== "object")) {
|
||||
throw new Error("session is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const receiver = session.attach_receiver(options);
|
||||
|
||||
function removeListeners(receiver: rhea.Receiver): void {
|
||||
receiver.removeListener("receiver_open", onOpen);
|
||||
receiver.removeListener("receiver_close", onClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(receiver);
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp receiver "${receiver.name}".`);
|
||||
resolve(receiver);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(receiver);
|
||||
debug(`Error occurred while creating a receiver over amqp connection.`, context.receiver!.error);
|
||||
reject(context.receiver!.error);
|
||||
}
|
||||
|
||||
receiver.once("receiver_open", onOpen);
|
||||
receiver.once("receiver_close", onClose);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp receiver with provided message and error event handlers on the provided amqp session.
|
||||
* This method should be used when you want to ensure that no messages are lost. For example: This method
|
||||
* is useful for creating EventHub Receivers where you want to start receiving ASAP.
|
||||
* @param {Session} session The amqp session object on which the receiver link needs to be established.
|
||||
* @param {OnAmqpEvent} onMessage The event handler for the "message" event for the receiver.
|
||||
* @param {OnAmqpEvent} onError The event handler for the "error" event for the receiver.
|
||||
* @param {ReceiverOptions} [options] Options that can be provided while creating an amqp receiver.
|
||||
* @return {Promise<Receiver>} Promise<Receiver>
|
||||
* - **Resolves** the promise with the Receiver object when rhea emits the "receiver_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "receiver_close" event while trying
|
||||
* to create an amqp receiver.
|
||||
*/
|
||||
export function createReceiverWithHandlers(session: rhea.Session, onMessage: rhea.OnAmqpEvent, onError: rhea.OnAmqpEvent, options?: rhea.ReceiverOptions): Promise<rhea.Receiver> {
|
||||
if (!session || (session && typeof session !== "object")) {
|
||||
throw new Error("session is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
if (!onMessage || (onMessage && typeof onMessage !== "function")) {
|
||||
throw new Error("onMessage is a required parameter and must be of type 'function'.");
|
||||
}
|
||||
|
||||
if (!onError || (onError && typeof onError !== "function")) {
|
||||
throw new Error("onError is a required parameter and must be of type 'function'.");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const receiver = session.attach_receiver(options);
|
||||
receiver.on("message", onMessage);
|
||||
receiver.on("receiver_error", onError);
|
||||
|
||||
function removeListeners(receiver: any): void {
|
||||
receiver.removeListener("receiver_open", onOpen);
|
||||
receiver.removeListener("receiver_close", onClose);
|
||||
}
|
||||
|
||||
function onOpen(context: rhea.EventContext): void {
|
||||
removeListeners(receiver);
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp receiver "${receiver.name}".`);
|
||||
resolve(receiver);
|
||||
});
|
||||
}
|
||||
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
removeListeners(receiver);
|
||||
debug(`Error occurred while creating a receiver over amqp connection.`, context.receiver!.error);
|
||||
reject(context.receiver!.error);
|
||||
}
|
||||
|
||||
receiver.once("receiver_open", onOpen);
|
||||
receiver.once("receiver_close", onClose);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp receiver.
|
||||
* @param {Receiver} receiver The amqp receiver that needs to be closed.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "receiver_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the
|
||||
* "receiver_error" event while trying to close an amqp receiver.
|
||||
*/
|
||||
export function closeReceiver(receiver: rhea.Receiver): Promise<void> {
|
||||
if (!receiver || (receiver && typeof receiver !== "object")) {
|
||||
throw new Error("receiver is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (receiver.is_open()) {
|
||||
function onClose(context: rhea.EventContext): void {
|
||||
receiver.removeListener("receiver_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp receiver has been closed.");
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function onError(context: rhea.EventContext): void {
|
||||
receiver.removeListener("receiver_error", onError);
|
||||
debug(`Error occurred while closing amqp receiver.`, context.receiver!.error);
|
||||
reject(context.receiver!.error);
|
||||
}
|
||||
|
||||
receiver.once("receiver_close", onClose);
|
||||
receiver.once("receiver_error", onError);
|
||||
receiver.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a mapping for Http like response status codes for different status-code values provided by an AMQP broker.
|
||||
* Defines a mapping for Http like response status codes for different status-code values
|
||||
* provided by an AMQP broker.
|
||||
* @enum AmqpResponseStatusCode
|
||||
*/
|
||||
export enum AmqpResponseStatusCode {
|
||||
|
@ -490,55 +69,34 @@ export enum AmqpResponseStatusCode {
|
|||
HttpVersionNotSupported = 505
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the delivery annotations.
|
||||
* @interface
|
||||
*/
|
||||
export interface EventHubDeliveryAnnotations extends rhea.DeliveryAnnotations {
|
||||
/**
|
||||
* @property {string} [last_enqueued_offset] The offset of the last event.
|
||||
*/
|
||||
last_enqueued_offset?: string;
|
||||
/**
|
||||
* @property {number} [last_enqueued_sequence_number] The sequence number of the last event.
|
||||
*/
|
||||
last_enqueued_sequence_number?: number;
|
||||
/**
|
||||
* @property {number} [last_enqueued_time_utc] The enqueued time of the last event.
|
||||
*/
|
||||
last_enqueued_time_utc?: number;
|
||||
/**
|
||||
* @property {number} [runtime_info_retrieval_time_utc] The retrieval time of the last event.
|
||||
*/
|
||||
runtime_info_retrieval_time_utc?: number;
|
||||
/**
|
||||
* @property {string} Any unknown delivery annotations.
|
||||
*/
|
||||
[x: string]: any;
|
||||
}
|
||||
export const messageProperties: string[] = [
|
||||
"message_id", "reply_to", "to", "correlation_id", "content_type", "absolute_expiry_time",
|
||||
"group_id", "group_sequence", "reply_to_group_id", "content_encoding", "creation_time", "subject",
|
||||
"user_id"
|
||||
];
|
||||
|
||||
export const messageHeader: string[] = [
|
||||
"first_acquirer", "delivery_count", "ttl", "durable", "priority"
|
||||
];
|
||||
|
||||
/**
|
||||
* Map containing message attributes that will be held in the message header.
|
||||
* Type declaration for a Function type where T is the input to the function and V is the output of the function.
|
||||
*/
|
||||
export interface EventHubMessageAnnotations extends rhea.MessageAnnotations {
|
||||
/**
|
||||
* @property {string | null} [x-opt-partition-key] Annotation for the partition key set for the event.
|
||||
export type Func<T, V> = (a: T) => V;
|
||||
|
||||
/**
|
||||
* Determines whether the given error object is like an AmqpError object.
|
||||
* @param err The AmqpError object
|
||||
*/
|
||||
"x-opt-partition-key"?: string | null;
|
||||
/**
|
||||
* @property {number} [x-opt-sequence-number] Annontation for the sequence number of the event.
|
||||
*/
|
||||
"x-opt-sequence-number"?: number;
|
||||
/**
|
||||
* @property {number} [x-opt-enqueued-time] Annotation for the enqueued time of the event.
|
||||
*/
|
||||
"x-opt-enqueued-time"?: number;
|
||||
/**
|
||||
* @property {string} [x-opt-offset] Annotation for the offset of the event.
|
||||
*/
|
||||
"x-opt-offset"?: string;
|
||||
/**
|
||||
* @property {any} Any other annotation that can be added to the message.
|
||||
*/
|
||||
[x: string]: any;
|
||||
export function isAmqpError(err: any): boolean {
|
||||
if (!err || typeof err !== "object") {
|
||||
throw new Error("err is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
let result: boolean = false;
|
||||
if (((err.condition && typeof err.condition === "string") && (err.description && typeof err.description === "string"))
|
||||
|| (err.value && Array.isArray(err.value))
|
||||
|| (err.constructor && err.constructor.name === "c")) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import { Session } from "./session";
|
||||
import { Connection } from "./connection";
|
||||
import { Func, ReceiverEvents } from ".";
|
||||
const debug = debugModule("rhea-promise:receiver");
|
||||
|
||||
export interface ReceiverOptions extends rhea.ReceiverOptions {
|
||||
onMessage?: rhea.OnAmqpEvent;
|
||||
onError?: rhea.OnAmqpEvent;
|
||||
}
|
||||
|
||||
export class Receiver {
|
||||
receiverOptions?: ReceiverOptions;
|
||||
private _session: Session;
|
||||
private _receiver: rhea.Receiver;
|
||||
|
||||
constructor(session: Session, receiver: rhea.Receiver, options?: ReceiverOptions) {
|
||||
this._session = session;
|
||||
this._receiver = receiver;
|
||||
this.receiverOptions = options;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._receiver.name;
|
||||
}
|
||||
|
||||
get error(): rhea.AmqpError | Error | undefined {
|
||||
return this._receiver.error;
|
||||
}
|
||||
|
||||
get properties(): rhea.Dictionary<any> {
|
||||
return this._receiver.properties;
|
||||
}
|
||||
|
||||
get source(): rhea.Source {
|
||||
return this._receiver.source;
|
||||
}
|
||||
|
||||
get target(): rhea.TerminusOptions {
|
||||
return this._receiver.target;
|
||||
}
|
||||
|
||||
get address(): string {
|
||||
return this.source.address;
|
||||
}
|
||||
|
||||
get session(): Session {
|
||||
return this._session;
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
return this._session.connection;
|
||||
}
|
||||
|
||||
get drain(): boolean {
|
||||
return this._receiver.drain;
|
||||
}
|
||||
|
||||
addCredit(credit: number): void {
|
||||
this._receiver.add_credit(credit);
|
||||
}
|
||||
|
||||
setCreditWindow(creditWindow: number): void {
|
||||
this._receiver.set_credit_window(creditWindow);
|
||||
}
|
||||
/**
|
||||
* Determines whether the sender link is open.
|
||||
* @returns {boolean} `true` open. `false` closed.
|
||||
*/
|
||||
isOpen(): boolean {
|
||||
let result = false;
|
||||
if (this._session.isOpen() && this._receiver.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove(): void {
|
||||
if (this._receiver) {
|
||||
this._receiver.remove();
|
||||
}
|
||||
if (this._session) {
|
||||
this._session.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp receiver.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "receiver_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the
|
||||
* "receiver_error" event while trying to close an amqp receiver.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
const receiverClose = new Promise<void>((resolve, reject) => {
|
||||
if (this.isOpen()) {
|
||||
let onError: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
this._receiver.removeListener(ReceiverEvents.receiverClose, onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp receiver has been closed.");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
onError = (context: rhea.EventContext) => {
|
||||
this._receiver.removeListener(ReceiverEvents.receiverError, onError);
|
||||
debug(`Error occurred while closing amqp receiver.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
};
|
||||
|
||||
this._receiver.once(ReceiverEvents.receiverClose, onClose);
|
||||
this._receiver.once(ReceiverEvents.receiverError, onError);
|
||||
this._receiver.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return receiverClose.then(() => { return this._session.close(); });
|
||||
}
|
||||
|
||||
registerHandler(event: ReceiverEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._receiver.on(event, handler);
|
||||
}
|
||||
|
||||
removeHandler(event: ReceiverEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._receiver.removeListener(event, handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import { Session } from "./session";
|
||||
import { Connection } from "./connection";
|
||||
import { Func, SenderEvents } from ".";
|
||||
const debug = debugModule("rhea-promise:sender");
|
||||
|
||||
export interface SenderOptions extends rhea.SenderOptions {
|
||||
onAccepted?: rhea.OnAmqpEvent;
|
||||
onRejected?: rhea.OnAmqpEvent;
|
||||
onReleased?: rhea.OnAmqpEvent;
|
||||
onModified?: rhea.OnAmqpEvent;
|
||||
onError?: rhea.OnAmqpEvent;
|
||||
}
|
||||
|
||||
export class Sender {
|
||||
senderOptions?: SenderOptions;
|
||||
private _session: Session;
|
||||
private _sender: rhea.Sender;
|
||||
|
||||
constructor(session: Session, sender: rhea.Sender, options?: SenderOptions) {
|
||||
this._session = session;
|
||||
this._sender = sender;
|
||||
this.senderOptions = options;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._sender.name;
|
||||
}
|
||||
|
||||
get error(): rhea.AmqpError | Error | undefined {
|
||||
return this._sender.error;
|
||||
}
|
||||
|
||||
get properties(): rhea.Dictionary<any> {
|
||||
return this._sender.properties;
|
||||
}
|
||||
|
||||
get source(): rhea.Source {
|
||||
return this._sender.source;
|
||||
}
|
||||
|
||||
get target(): rhea.TerminusOptions {
|
||||
return this._sender.target;
|
||||
}
|
||||
|
||||
get address(): string {
|
||||
return this.source.address;
|
||||
}
|
||||
get credit(): number {
|
||||
return (this._sender as any).credit;
|
||||
}
|
||||
|
||||
get session(): Session {
|
||||
return this._session;
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
return this._session.connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the message is sendable.
|
||||
* @returns {boolean} `true` Sendable. `false` Not Sendable.
|
||||
*/
|
||||
sendable(): boolean {
|
||||
return this._sender.sendable();
|
||||
}
|
||||
|
||||
send(msg: rhea.Message | Buffer, tag?: Buffer | string, format?: number): rhea.Delivery {
|
||||
return this._sender.send(msg, tag, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the sender link is open.
|
||||
* @returns {boolean} `true` open. `false` closed.
|
||||
*/
|
||||
isOpen(): boolean {
|
||||
let result = false;
|
||||
if (this._session.isOpen() && this._sender.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove(): void {
|
||||
if (this._sender) {
|
||||
this._sender.remove();
|
||||
}
|
||||
if (this._session) {
|
||||
this._session.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp sender.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "sender_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the
|
||||
* "sender_error" event while trying to close an amqp sender.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
const senderClose = new Promise<void>((resolve, reject) => {
|
||||
if (this.isOpen()) {
|
||||
let onError: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
this._sender.removeListener(SenderEvents.senderClose, onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp sender has been closed.");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
onError = (context: rhea.EventContext) => {
|
||||
this._sender.removeListener(SenderEvents.senderError, onError);
|
||||
debug(`Error occurred while closing amqp sender.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
};
|
||||
|
||||
this._sender.once(SenderEvents.senderClose, onClose);
|
||||
this._sender.once(SenderEvents.senderError, onError);
|
||||
this._sender.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return senderClose.then(() => { return this._session.close(); });
|
||||
}
|
||||
|
||||
registerHandler(event: SenderEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._sender.on(event, handler);
|
||||
}
|
||||
|
||||
removeHandler(event: SenderEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._sender.removeListener(event, handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as rhea from "rhea";
|
||||
import * as debugModule from "debug";
|
||||
import { Connection } from "./connection";
|
||||
import { Receiver, ReceiverOptions } from "./receiver";
|
||||
import { Sender, SenderOptions } from "./sender";
|
||||
import { Func, SenderEvents, ReceiverEvents } from ".";
|
||||
|
||||
const debug = debugModule("rhea-promise:session");
|
||||
|
||||
export class Session {
|
||||
private _session: rhea.Session;
|
||||
private _connection: Connection;
|
||||
|
||||
constructor(connection: Connection, session: rhea.Session) {
|
||||
this._connection = connection;
|
||||
this._session = session;
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
return this._connection;
|
||||
}
|
||||
|
||||
get outgoing(): any {
|
||||
return (this._session as any).outgoing;
|
||||
}
|
||||
|
||||
isOpen(): boolean {
|
||||
let result = false;
|
||||
if (this._connection.isOpen() && this._session.is_open()) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove(): void {
|
||||
if (this._session) {
|
||||
this._session.remove();
|
||||
}
|
||||
}
|
||||
|
||||
begin(): void {
|
||||
if (this._session) {
|
||||
this._session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the amqp session.
|
||||
* @return {Promise<void>} Promise<void>
|
||||
* - **Resolves** the promise when rhea emits the "session_close" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "session_error" event while trying
|
||||
* to close an amqp session.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (this.isOpen()) {
|
||||
const onClose = (context: rhea.EventContext) => {
|
||||
this._session.removeListener("session_close", onClose);
|
||||
process.nextTick(() => {
|
||||
debug("Resolving the promise as the amqp session has been closed.");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
const onError = (context: rhea.EventContext) => {
|
||||
this._session.removeListener("session_error", onError);
|
||||
debug(`Error occurred while closing amqp session.`, context.session.error);
|
||||
reject(context.session.error);
|
||||
};
|
||||
|
||||
this._session.once("session_close", onClose);
|
||||
this._session.once("session_error", onError);
|
||||
this._session.close();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp receiver on this session.
|
||||
* @param {Session} session The amqp session object on which the receiver link needs to be established.
|
||||
* @param {ReceiverOptions} [options] Options that can be provided while creating an amqp receiver.
|
||||
* @return {Promise<Receiver>} Promise<Receiver>
|
||||
* - **Resolves** the promise with the Receiver object when rhea emits the "receiver_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "receiver_close" event while trying
|
||||
* to create an amqp receiver.
|
||||
*/
|
||||
createReceiver(options?: ReceiverOptions): Promise<Receiver> {
|
||||
if (options &&
|
||||
((options.onMessage && !options.onError) || (options.onError && !options.onMessage))) {
|
||||
throw new Error("Both onMessage and onError handlers must be provided if one of " +
|
||||
"them is provided.");
|
||||
}
|
||||
|
||||
const handlersProvided = options && options.onMessage ? true : false;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const rheaReceiver = this._session.attach_receiver(options);
|
||||
const receiver = new Receiver(this, rheaReceiver, options);
|
||||
let onOpen: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
|
||||
if (handlersProvided) {
|
||||
rheaReceiver.on(ReceiverEvents.message, options!.onMessage!);
|
||||
rheaReceiver.on(ReceiverEvents.receiverError, options!.onError!);
|
||||
}
|
||||
|
||||
const removeListeners = () => {
|
||||
rheaReceiver.removeListener("receiver_open", onOpen);
|
||||
rheaReceiver.removeListener("receiver_close", onClose);
|
||||
};
|
||||
|
||||
onOpen = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp receiver "${rheaReceiver.name}".`);
|
||||
resolve(receiver);
|
||||
});
|
||||
};
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
debug(`Error occurred while creating a receiver over amqp connection.`, context.receiver!.error);
|
||||
reject(context.receiver!.error);
|
||||
};
|
||||
|
||||
rheaReceiver.once("receiver_open", onOpen);
|
||||
rheaReceiver.once("receiver_close", onClose);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an amqp sender on this session.
|
||||
* @param {SenderOptions} [options] Options that can be provided while creating an amqp sender.
|
||||
* @return {Promise<Sender>} Promise<Sender>
|
||||
* - **Resolves** the promise with the Sender object when rhea emits the "sender_open" event.
|
||||
* - **Rejects** the promise with an AmqpError when rhea emits the "sender_close" event while trying
|
||||
* to create an amqp sender.
|
||||
*/
|
||||
createSender(options?: SenderOptions): Promise<Sender> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rheaSender = this._session.attach_sender(options);
|
||||
const sender = new Sender(this, rheaSender, options);
|
||||
let onSendable: Func<rhea.EventContext, void>;
|
||||
let onClose: Func<rhea.EventContext, void>;
|
||||
|
||||
if (options) {
|
||||
if (options.onError) {
|
||||
rheaSender.on(SenderEvents.senderError, options.onError);
|
||||
}
|
||||
if (options.onAccepted) {
|
||||
rheaSender.on(SenderEvents.accepted, options.onAccepted);
|
||||
}
|
||||
if (options.onRejected) {
|
||||
rheaSender.on(SenderEvents.rejected, options.onRejected);
|
||||
}
|
||||
if (options.onReleased) {
|
||||
rheaSender.on(SenderEvents.released, options.onReleased);
|
||||
}
|
||||
if (options.onModified) {
|
||||
rheaSender.on("modified", options.onModified);
|
||||
}
|
||||
}
|
||||
|
||||
const removeListeners = () => {
|
||||
rheaSender.removeListener(SenderEvents.senderOpen, onSendable);
|
||||
rheaSender.removeListener(SenderEvents.senderClose, onClose);
|
||||
};
|
||||
|
||||
onSendable = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
process.nextTick(() => {
|
||||
debug(`Resolving the promise with amqp sender "${rheaSender.name}".`);
|
||||
resolve(sender);
|
||||
});
|
||||
};
|
||||
|
||||
onClose = (context: rhea.EventContext) => {
|
||||
removeListeners();
|
||||
debug(`Error occurred while creating a sender over amqp connection.`, context.sender!.error);
|
||||
reject(context.sender!.error);
|
||||
};
|
||||
|
||||
rheaSender.once(SenderEvents.sendable, onSendable);
|
||||
rheaSender.once(SenderEvents.senderClose, onClose);
|
||||
});
|
||||
}
|
||||
|
||||
registerHandler(event: rhea.SessionEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._session.on(event, handler);
|
||||
}
|
||||
|
||||
removeHandler(event: rhea.SessionEvents, handler: rhea.OnAmqpEvent): void {
|
||||
this._session.removeListener(event, handler);
|
||||
}
|
||||
}
|
|
@ -1,273 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as os from "os";
|
||||
import * as process from "process";
|
||||
import * as debugModule from "debug";
|
||||
import * as uuid from "uuid/v4";
|
||||
import { defaultLock } from "./util/utils";
|
||||
import {
|
||||
ReceiverOptions, SenderOptions, createSession, createSenderWithHandlers,
|
||||
createSender, createReceiver, connect, ConnectionOptions,
|
||||
OnAmqpEvent, AmqpError, createReceiverWithHandlers, EventContext, Connection
|
||||
} from "./rhea-promise";
|
||||
import * as Constants from "./util/constants";
|
||||
import { ConditionStatusMapper, translate } from "./errors";
|
||||
import { Message, ConnectionConfig } from ".";
|
||||
import { retry } from "./retry";
|
||||
const debug = debugModule("azure:event-hubs:rpc");
|
||||
|
||||
export interface RequestResponseLink {
|
||||
sender: any;
|
||||
receiver: any;
|
||||
session: any;
|
||||
}
|
||||
|
||||
export interface ReceiverLink {
|
||||
receiver: any;
|
||||
session: any;
|
||||
}
|
||||
|
||||
export interface SenderLink {
|
||||
sender: any;
|
||||
session: any;
|
||||
}
|
||||
|
||||
export interface LinkOptions {
|
||||
connection: any;
|
||||
onError: OnAmqpEvent;
|
||||
}
|
||||
|
||||
export interface ReceiverLinkOptions extends LinkOptions {
|
||||
onMessage: OnAmqpEvent;
|
||||
receiverOptions: ReceiverOptions;
|
||||
}
|
||||
|
||||
export interface SenderLinkOptions extends LinkOptions {
|
||||
senderOptions: SenderOptions;
|
||||
}
|
||||
|
||||
export interface CreateConnectionPrameters {
|
||||
config: ConnectionConfig;
|
||||
userAgent: string;
|
||||
useSaslPlain?: boolean;
|
||||
}
|
||||
|
||||
export async function createRequestResponseLink(connection: any, senderOptions: SenderOptions, receiverOptions: ReceiverOptions): Promise<RequestResponseLink> {
|
||||
if (!connection) {
|
||||
throw new Error(`Please provide a connection to create the sender/receiver link on the same session.`);
|
||||
}
|
||||
if (!senderOptions) {
|
||||
throw new Error(`Please provide sender options.`);
|
||||
}
|
||||
if (!receiverOptions) {
|
||||
throw new Error(`Please provide receiver options.`);
|
||||
}
|
||||
const session = await createSession(connection);
|
||||
const sender = await createSender(session, senderOptions);
|
||||
const receiver = await createReceiver(session, receiverOptions);
|
||||
debug("[%s] Successfully created the sender and receiver links on the same session.", connection.options.id);
|
||||
return {
|
||||
session: session,
|
||||
sender: sender,
|
||||
receiver: receiver
|
||||
};
|
||||
}
|
||||
|
||||
export async function createReceiverLink(connection: any, receiverOptions: ReceiverOptions): Promise<ReceiverLink> {
|
||||
if (!connection) {
|
||||
throw new Error(`Please provide a connection to create the receiver link on a session.`);
|
||||
}
|
||||
|
||||
if (!receiverOptions) {
|
||||
throw new Error(`Please provide receiver options.`);
|
||||
}
|
||||
const session = await createSession(connection);
|
||||
const receiver = await createReceiver(session, receiverOptions);
|
||||
debug("[%s] Successfully created the receiver link on a dedicated session for it.", connection.options.id);
|
||||
return {
|
||||
session: session,
|
||||
receiver: receiver
|
||||
};
|
||||
}
|
||||
|
||||
export async function createReceiverLinkWithHandlers(options: ReceiverLinkOptions): Promise<ReceiverLink> {
|
||||
if (!options.connection) {
|
||||
throw new Error(`Please provide a connection to create the receiver link on a session.`);
|
||||
}
|
||||
|
||||
if (!options.receiverOptions) {
|
||||
throw new Error(`Please provide receiver options.`);
|
||||
}
|
||||
if (!options.onError) {
|
||||
throw new Error(`Please provide onError.`);
|
||||
}
|
||||
if (!options.onMessage) {
|
||||
throw new Error(`Please provide onMessage.`);
|
||||
}
|
||||
const session = await createSession(options.connection);
|
||||
const receiver = await createReceiverWithHandlers(session, options.onMessage, options.onError, options.receiverOptions);
|
||||
debug("[%s] Successfully created the receiver link on a dedicated session for it.",
|
||||
options.connection.options.id);
|
||||
return {
|
||||
session: session,
|
||||
receiver: receiver
|
||||
};
|
||||
}
|
||||
|
||||
export async function createSenderLink(connection: any, senderOptions: SenderOptions): Promise<SenderLink> {
|
||||
if (!connection) {
|
||||
throw new Error(`Please provide a connection to create the sender link on a session.`);
|
||||
}
|
||||
if (!senderOptions) {
|
||||
throw new Error(`Please provide sender options.`);
|
||||
}
|
||||
const session = await createSession(connection);
|
||||
const sender = await createSender(session, senderOptions);
|
||||
debug("[%s] Successfully created the sender link on a dedicated session for it.",
|
||||
connection.options.id);
|
||||
return {
|
||||
session: session,
|
||||
sender: sender
|
||||
};
|
||||
}
|
||||
|
||||
export async function createSenderLinkWithHandlers(options: SenderLinkOptions): Promise<SenderLink> {
|
||||
if (!options.connection) {
|
||||
throw new Error(`Please provide a connection to create the sender link on a session.`);
|
||||
}
|
||||
if (!options.senderOptions) {
|
||||
throw new Error(`Please provide sender options.`);
|
||||
}
|
||||
if (!options.onError) {
|
||||
throw new Error(`Please provide onError.`);
|
||||
}
|
||||
|
||||
const session = await createSession(options.connection);
|
||||
const sender = await createSenderWithHandlers(session, options.onError, options.senderOptions);
|
||||
debug("[%s] Successfully created the sender link on a dedicated session for it.",
|
||||
options.connection.options.id);
|
||||
return {
|
||||
session: session,
|
||||
sender: sender
|
||||
};
|
||||
}
|
||||
|
||||
export function sendRequest(connection: any, link: RequestResponseLink, request: Message, timeoutInSeconds?: number): Promise<any> {
|
||||
if (!connection) {
|
||||
throw new Error("connection is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
if (!link) {
|
||||
throw new Error("link is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
if (!request) {
|
||||
throw new Error("request is a required parameter and must be of type 'object'.");
|
||||
}
|
||||
|
||||
if (!request.message_id) request.message_id = uuid();
|
||||
|
||||
if (!timeoutInSeconds) {
|
||||
timeoutInSeconds = 10;
|
||||
}
|
||||
|
||||
const sendRequestPromise: Promise<any> = new Promise((resolve: any, reject: any) => {
|
||||
let waitTimer: any;
|
||||
let timeOver: boolean = false;
|
||||
|
||||
const messageCallback = (context: EventContext) => {
|
||||
// remove the event listener as this will be registered next time when someone makes a request.
|
||||
link.receiver.removeListener(Constants.message, messageCallback);
|
||||
const code: number = context.message!.application_properties![Constants.statusCode];
|
||||
const desc: string = context.message!.application_properties![Constants.statusDescription];
|
||||
const errorCondition: string | undefined = context.message!.application_properties![Constants.errorCondition];
|
||||
const responseCorrelationId = context.message!.correlation_id;
|
||||
debug("[%s] %s response: ", connection.options.id, request.to || "$management", context.message);
|
||||
if (code > 199 && code < 300) {
|
||||
if (request.message_id === responseCorrelationId || request.correlation_id === responseCorrelationId) {
|
||||
if (!timeOver) {
|
||||
clearTimeout(waitTimer);
|
||||
}
|
||||
debug("[%s] request-messageId | '%s' == '%s' | response-correlationId.", connection.options.id, request.message_id, responseCorrelationId);
|
||||
return resolve(context.message!.body);
|
||||
} else {
|
||||
debug("[%s] request-messageId | '%s' != '%s' | response-correlationId. Hence dropping this response and waiting for the next one.",
|
||||
connection.options.id, request.message_id, responseCorrelationId);
|
||||
}
|
||||
} else {
|
||||
const condition = errorCondition || ConditionStatusMapper[code] || "amqp:internal-error";
|
||||
const e: AmqpError = {
|
||||
condition: condition,
|
||||
description: desc
|
||||
};
|
||||
return reject(translate(e));
|
||||
}
|
||||
};
|
||||
|
||||
const actionAfterTimeout = () => {
|
||||
timeOver = true;
|
||||
link.receiver.removeListener(Constants.message, messageCallback);
|
||||
const address = link.receiver.options && link.receiver.options.source && link.receiver.options.source.address
|
||||
? link.receiver.options.source.address
|
||||
: "address";
|
||||
const desc: string = `The request with message_id "${request.message_id}" to "${address}" ` +
|
||||
`endpoint timed out. Please try again later.`;
|
||||
const e: AmqpError = {
|
||||
condition: ConditionStatusMapper[408],
|
||||
description: desc
|
||||
};
|
||||
return reject(translate(e));
|
||||
};
|
||||
|
||||
link.receiver.on(Constants.message, messageCallback);
|
||||
waitTimer = setTimeout(actionAfterTimeout, timeoutInSeconds! * 1000);
|
||||
debug("[%s] %s request sent: %O", connection.options.id, request.to || "$managment", request);
|
||||
link.sender.send(request);
|
||||
});
|
||||
|
||||
return retry(() => sendRequestPromise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the AMQP connection to the Event Hub for this client, returning a promise
|
||||
* that will be resolved when the connection is completed.
|
||||
*
|
||||
* @param {ConnectionContext} context The connection context.
|
||||
* @param {boolean} [useSaslPlain] True for using sasl plain mode for authentication, false otherwise.
|
||||
* @returns {Promise<Connection>} The Connection object.
|
||||
*/
|
||||
export async function open(params: CreateConnectionPrameters): Promise<Connection> {
|
||||
try {
|
||||
return await defaultLock.acquire("connect", () => { return _open(params); });
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function _open(params: CreateConnectionPrameters): Promise<Connection> {
|
||||
const connectOptions: ConnectionOptions = {
|
||||
transport: Constants.TLS,
|
||||
host: params.config.host,
|
||||
hostname: params.config.host,
|
||||
username: params.config.sharedAccessKeyName,
|
||||
port: 5671,
|
||||
reconnect_limit: Constants.reconnectLimit,
|
||||
properties: {
|
||||
product: "MSJSClient",
|
||||
version: Constants.packageJsonInfo.version || "0.1.0",
|
||||
platform: `(${os.arch()}-${os.type()}-${os.release()})`,
|
||||
framework: `Node/${process.version}`,
|
||||
"user-agent": params.userAgent
|
||||
}
|
||||
};
|
||||
if (params.useSaslPlain) {
|
||||
connectOptions.password = params.config.sharedAccessKey;
|
||||
}
|
||||
debug("Dialing the amqp connection with options.", connectOptions);
|
||||
const connection = await connect(connectOptions);
|
||||
debug("Successfully established the amqp connection '%s'.", connection.options.id);
|
||||
return connection;
|
||||
}
|
||||
|
|
@ -3,12 +3,19 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as debugModule from "debug";
|
||||
import { Constants } from "./amqp-common";
|
||||
import { ReceiverEvents } from "./rhea-promise";
|
||||
import { ReceiveOptions, OnMessage, OnError } from ".";
|
||||
import { EventHubReceiver, ReceiverRuntimeInfo } from "./eventHubReceiver";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import * as Constants from "./util/constants";
|
||||
|
||||
const debug = debugModule("azure:event-hubs:receiverstreaming");
|
||||
|
||||
/**
|
||||
* Describes the receive handler object that is returned from the receive() method with handlers is
|
||||
* called. The ReceiveHandler is used to stop receiving more messages.
|
||||
* @class ReceiveHandler
|
||||
*/
|
||||
export class ReceiveHandler {
|
||||
/**
|
||||
* @property {string} name The Receiver handler name.
|
||||
|
@ -141,7 +148,7 @@ export class StreamingReceiver extends EventHubReceiver {
|
|||
}
|
||||
this._onMessage = onMessage;
|
||||
this._onError = onError;
|
||||
if (!this._isOpen()) {
|
||||
if (!this.isOpen()) {
|
||||
this._init().catch((err) => {
|
||||
this._onError!(err);
|
||||
});
|
||||
|
@ -151,10 +158,10 @@ export class StreamingReceiver extends EventHubReceiver {
|
|||
// these handlers will be automatically removed.
|
||||
debug("[%s] Receiver link is already present for '%s' due to previous receive() calls. " +
|
||||
"Hence reusing it and attaching message and error handlers.", this._context.connectionId, this.name);
|
||||
this._receiver.on(Constants.message, this._onAmqpMessage);
|
||||
this._receiver.on(Constants.receiverError, this._onAmqpError);
|
||||
this._receiver.set_credit_window(Constants.defaultPrefetchCount);
|
||||
this._receiver.add_credit(Constants.defaultPrefetchCount);
|
||||
this._receiver!.registerHandler(ReceiverEvents.message, this._onAmqpMessage);
|
||||
this._receiver!.registerHandler(ReceiverEvents.receiverError, this._onAmqpError);
|
||||
this._receiver!.setCreditWindow(Constants.defaultPrefetchCount);
|
||||
this._receiver!.addCredit(Constants.defaultPrefetchCount);
|
||||
debug("[%s] Receiver '%s', set the prefetch count to 1000 and " +
|
||||
"providing a credit of the same amount.", this._context.connectionId, this.name);
|
||||
}
|
||||
|
|
|
@ -1,46 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
export const partitionKey = "x-opt-partition-key";
|
||||
export const sequenceNumber = "x-opt-sequence-number";
|
||||
export const enqueuedTime = "x-opt-enqueued-time";
|
||||
export const offset = "x-opt-offset";
|
||||
export const enqueuedTimeAnnotation = `amqp.annotation.${enqueuedTime}`;
|
||||
export const offsetAnnotation = `amqp.annotation.${offset}`;
|
||||
export const sequenceNumberAnnotation = `amqp.annotation.${sequenceNumber}`;
|
||||
export const message = "message";
|
||||
export const error = "error";
|
||||
export const statusCode = "status-code";
|
||||
export const statusDescription = "status-description";
|
||||
export const errorCondition = "error-condition";
|
||||
export const management = "$management";
|
||||
export const partition = "partition";
|
||||
export const partitionId = "partitionId";
|
||||
export const readOperation = "READ";
|
||||
export const TLS = "tls";
|
||||
export const establishConnection = "establishConnection";
|
||||
export const defaultConsumerGroup = "$default";
|
||||
export const eventHub = "eventhub";
|
||||
export const cbsEndpoint = "$cbs";
|
||||
export const cbsReplyTo = "cbs";
|
||||
export const operationPutToken = "put-token";
|
||||
export const aadEventHubsAudience = "https://eventhubs.azure.net/";
|
||||
export const maxUserAgentLength = 128;
|
||||
export const vendorString = "com.microsoft";
|
||||
export const attachEpoch = `${vendorString}:epoch`;
|
||||
export const receiverIdentifierName = `${vendorString}:receiver-name`;
|
||||
export const enableReceiverRuntimeMetricName = `${vendorString}:enable-receiver-runtime-metric`;
|
||||
export const receiverError = "receiver_error";
|
||||
export const senderError = "sender_error";
|
||||
export const sessionError = "session_error";
|
||||
export const connectionError = "connection_error";
|
||||
export const defaultOperationTimeoutInSeconds = 60;
|
||||
export const managementRequestKey = "managementRequest";
|
||||
export const negotiateCbsKey = "negotiateCbs";
|
||||
export const negotiateClaim = "negotiateClaim";
|
||||
export const ensureContainerAndBlob = "ensureContainerAndBlob";
|
||||
export const defaultPrefetchCount = 1000;
|
||||
export const reconnectLimit = 100;
|
||||
export const packageJsonInfo = {
|
||||
name: "azure-event-hubs-js",
|
||||
version: "0.2.3"
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/chai": "4.1.3"
|
||||
"@types/chai": "*"
|
||||
}
|
||||
},
|
||||
"@types/debug": {
|
||||
|
@ -42,7 +42,7 @@
|
|||
"integrity": "sha512-mmhpINC/HcLGQK5ikFJlLXINVvcxhlrV+ZOUJSN7/ottYl+8X4oSXzS9lBtDkmWAl96EGyGyLrNvk9zqdSH8Fw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "8.9.4"
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/form-data": {
|
||||
|
@ -50,7 +50,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
||||
"requires": {
|
||||
"@types/node": "8.9.4"
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/mocha": {
|
||||
|
@ -69,10 +69,10 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz",
|
||||
"integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==",
|
||||
"requires": {
|
||||
"@types/caseless": "0.12.1",
|
||||
"@types/form-data": "2.2.1",
|
||||
"@types/node": "8.9.4",
|
||||
"@types/tough-cookie": "2.3.2"
|
||||
"@types/caseless": "*",
|
||||
"@types/form-data": "*",
|
||||
"@types/node": "*",
|
||||
"@types/tough-cookie": "*"
|
||||
}
|
||||
},
|
||||
"@types/tough-cookie": {
|
||||
|
@ -85,7 +85,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.3.tgz",
|
||||
"integrity": "sha512-5fRLCYhLtDb3hMWqQyH10qtF+Ud2JnNCXTCZ+9ktNdCcgslcuXkDTkFcJNk++MT29yDntDnlF1+jD+uVGumsbw==",
|
||||
"requires": {
|
||||
"@types/node": "8.9.4"
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"adal-node": {
|
||||
|
@ -93,15 +93,15 @@
|
|||
"resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz",
|
||||
"integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=",
|
||||
"requires": {
|
||||
"@types/node": "8.9.4",
|
||||
"async": "2.6.0",
|
||||
"date-utils": "1.2.21",
|
||||
"jws": "3.1.5",
|
||||
"request": "2.83.0",
|
||||
"underscore": "1.8.3",
|
||||
"uuid": "3.2.1",
|
||||
"xmldom": "0.1.27",
|
||||
"xpath.js": "1.1.0"
|
||||
"@types/node": "^8.0.47",
|
||||
"async": ">=0.6.0",
|
||||
"date-utils": "*",
|
||||
"jws": "3.x.x",
|
||||
"request": ">= 2.52.0",
|
||||
"underscore": ">= 1.3.1",
|
||||
"uuid": "^3.1.0",
|
||||
"xmldom": ">= 0.1.x",
|
||||
"xpath.js": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
|
@ -109,10 +109,10 @@
|
|||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"fast-deep-equal": "1.1.0",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"json-schema-traverse": "0.3.1"
|
||||
"co": "^4.6.0",
|
||||
"fast-deep-equal": "^1.0.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
|
@ -127,7 +127,7 @@
|
|||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
|
@ -136,7 +136,7 @@
|
|||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "1.0.3"
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"arrify": {
|
||||
|
@ -166,7 +166,7 @@
|
|||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
|
||||
"integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
|
||||
"requires": {
|
||||
"lodash": "4.17.5"
|
||||
"lodash": "^4.14.0"
|
||||
}
|
||||
},
|
||||
"async-lock": {
|
||||
|
@ -195,9 +195,9 @@
|
|||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"esutils": "2.0.2",
|
||||
"js-tokens": "3.0.2"
|
||||
"chalk": "^1.1.3",
|
||||
"esutils": "^2.0.2",
|
||||
"js-tokens": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
|
@ -212,11 +212,11 @@
|
|||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
|
@ -239,7 +239,7 @@
|
|||
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"boom": {
|
||||
|
@ -247,7 +247,7 @@
|
|||
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
|
||||
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
|
@ -256,7 +256,7 @@
|
|||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
|
@ -294,12 +294,12 @@
|
|||
"integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assertion-error": "1.1.0",
|
||||
"check-error": "1.0.2",
|
||||
"deep-eql": "3.0.1",
|
||||
"get-func-name": "2.0.0",
|
||||
"pathval": "1.1.0",
|
||||
"type-detect": "4.0.8"
|
||||
"assertion-error": "^1.0.1",
|
||||
"check-error": "^1.0.1",
|
||||
"deep-eql": "^3.0.0",
|
||||
"get-func-name": "^2.0.0",
|
||||
"pathval": "^1.0.0",
|
||||
"type-detect": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"chai-as-promised": {
|
||||
|
@ -308,7 +308,7 @@
|
|||
"integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"check-error": "1.0.2"
|
||||
"check-error": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
|
@ -317,9 +317,9 @@
|
|||
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.4.0"
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"check-error": {
|
||||
|
@ -339,7 +339,7 @@
|
|||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
"color-name": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
|
@ -353,7 +353,7 @@
|
|||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
|
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
|
@ -378,7 +378,7 @@
|
|||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
|
||||
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
|
||||
"requires": {
|
||||
"boom": "5.2.0"
|
||||
"boom": "5.x.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"boom": {
|
||||
|
@ -386,7 +386,7 @@
|
|||
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
|
||||
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +396,7 @@
|
|||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"date-utils": {
|
||||
|
@ -418,7 +418,7 @@
|
|||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "4.0.8"
|
||||
"type-detect": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
|
@ -449,7 +449,7 @@
|
|||
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
"jsbn": "~0.1.0"
|
||||
}
|
||||
},
|
||||
"ecdsa-sig-formatter": {
|
||||
|
@ -457,7 +457,7 @@
|
|||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz",
|
||||
"integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
|
@ -508,9 +508,9 @@
|
|||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"mime-types": "2.1.18"
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
|
@ -530,7 +530,7 @@
|
|||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
|
@ -539,12 +539,12 @@
|
|||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"growl": {
|
||||
|
@ -563,8 +563,8 @@
|
|||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"har-schema": "2.0.0"
|
||||
"ajv": "^5.1.0",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
|
@ -573,7 +573,7 @@
|
|||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
|
@ -587,10 +587,10 @@
|
|||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
||||
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
|
||||
"requires": {
|
||||
"boom": "4.3.1",
|
||||
"cryptiles": "3.1.2",
|
||||
"hoek": "4.2.1",
|
||||
"sntp": "2.1.0"
|
||||
"boom": "4.x.x",
|
||||
"cryptiles": "3.x.x",
|
||||
"hoek": "4.x.x",
|
||||
"sntp": "2.x.x"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
|
@ -609,9 +609,9 @@
|
|||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"jsprim": "1.4.1",
|
||||
"sshpk": "1.14.1"
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
|
@ -620,8 +620,8 @@
|
|||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
|
@ -662,8 +662,8 @@
|
|||
"integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "1.0.10",
|
||||
"esprima": "4.0.0"
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
|
@ -705,7 +705,7 @@
|
|||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.10",
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
|
@ -713,8 +713,8 @@
|
|||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz",
|
||||
"integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==",
|
||||
"requires": {
|
||||
"jwa": "1.1.6",
|
||||
"safe-buffer": "5.1.1"
|
||||
"jwa": "^1.1.5",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
|
@ -738,7 +738,7 @@
|
|||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.33.0"
|
||||
"mime-db": "~1.33.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
|
@ -747,7 +747,7 @@
|
|||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
|
@ -796,7 +796,7 @@
|
|||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -816,17 +816,17 @@
|
|||
"resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.3.3.tgz",
|
||||
"integrity": "sha512-S45//v5VIOojBYl2eL9ynyoskKluSUGxP5LBhS0Tf1Hb/k3JgkafoXlAxT4eKj5dyUmA/pPcRBGhZNohOH2esQ==",
|
||||
"requires": {
|
||||
"@types/node": "8.9.4",
|
||||
"@types/request": "2.47.0",
|
||||
"@types/uuid": "3.4.3",
|
||||
"duplexer": "0.1.1",
|
||||
"is-buffer": "1.1.6",
|
||||
"is-stream": "1.1.0",
|
||||
"moment": "2.21.0",
|
||||
"request": "2.83.0",
|
||||
"through": "2.3.8",
|
||||
"@types/node": "^8.9.4",
|
||||
"@types/request": "^2.47.0",
|
||||
"@types/uuid": "^3.4.3",
|
||||
"duplexer": "^0.1.1",
|
||||
"is-buffer": "^1.1.6",
|
||||
"is-stream": "^1.1.0",
|
||||
"moment": "^2.21.0",
|
||||
"request": "^2.83.0",
|
||||
"through": "^2.3.8",
|
||||
"tunnel": "0.0.5",
|
||||
"uuid": "3.2.1"
|
||||
"uuid": "^3.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-buffer": {
|
||||
|
@ -841,13 +841,13 @@
|
|||
"resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.5.5.tgz",
|
||||
"integrity": "sha512-LnP5YlgqvNAOi1x2b4Ru+1ubz/PtYQgNeH74aiolP+Sb5lpOw5ewzbncXae1pR5OWu9NGwxjX59adzSWosqnzw==",
|
||||
"requires": {
|
||||
"@types/node": "9.6.0",
|
||||
"@types/uuid": "3.4.3",
|
||||
"adal-node": "0.1.28",
|
||||
"@types/node": "^9.4.6",
|
||||
"@types/uuid": "^3.4.3",
|
||||
"adal-node": "^0.1.27",
|
||||
"async": "2.6.0",
|
||||
"moment": "2.21.0",
|
||||
"ms-rest": "2.3.3",
|
||||
"uuid": "3.2.1"
|
||||
"moment": "^2.20.1",
|
||||
"ms-rest": "^2.3.2",
|
||||
"uuid": "^3.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
|
@ -868,7 +868,7 @@
|
|||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
|
@ -909,28 +909,28 @@
|
|||
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
|
||||
"integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
|
||||
"requires": {
|
||||
"aws-sign2": "0.7.0",
|
||||
"aws4": "1.6.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.3.2",
|
||||
"har-validator": "5.0.3",
|
||||
"hawk": "6.0.2",
|
||||
"http-signature": "1.2.0",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.18",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "2.1.0",
|
||||
"qs": "6.5.1",
|
||||
"safe-buffer": "5.1.1",
|
||||
"stringstream": "0.0.6",
|
||||
"tough-cookie": "2.3.4",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.2.1"
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.6.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.5",
|
||||
"extend": "~3.0.1",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.1",
|
||||
"har-validator": "~5.0.3",
|
||||
"hawk": "~6.0.2",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.17",
|
||||
"oauth-sign": "~0.8.2",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.1",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"stringstream": "~0.0.5",
|
||||
"tough-cookie": "~2.3.3",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
|
@ -939,15 +939,15 @@
|
|||
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "1.0.5"
|
||||
"path-parse": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"rhea": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/rhea/-/rhea-0.2.13.tgz",
|
||||
"integrity": "sha1-iIDS27RmIHYMPXEO4YhT7XRupg0=",
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/rhea/-/rhea-0.2.14.tgz",
|
||||
"integrity": "sha1-8vtnWH7OsJ+gcVxHIdGsJx9R3f4=",
|
||||
"requires": {
|
||||
"debug": "3.1.0"
|
||||
"debug": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
|
@ -966,7 +966,7 @@
|
|||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
|
||||
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
|
||||
"requires": {
|
||||
"hoek": "4.2.1"
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
|
@ -981,8 +981,8 @@
|
|||
"integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "1.0.0",
|
||||
"source-map": "0.6.1"
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
|
@ -996,14 +996,14 @@
|
|||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
|
||||
"integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jsbn": "0.1.1",
|
||||
"tweetnacl": "0.14.5"
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"tweetnacl": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
|
@ -1017,7 +1017,7 @@
|
|||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
|
@ -1026,7 +1026,7 @@
|
|||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"through": {
|
||||
|
@ -1039,7 +1039,7 @@
|
|||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
|
||||
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
"punycode": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
|
@ -1048,14 +1048,14 @@
|
|||
"integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arrify": "1.0.1",
|
||||
"chalk": "2.4.1",
|
||||
"diff": "3.5.0",
|
||||
"make-error": "1.3.4",
|
||||
"minimist": "1.2.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"source-map-support": "0.5.6",
|
||||
"yn": "2.0.0"
|
||||
"arrify": "^1.0.0",
|
||||
"chalk": "^2.3.0",
|
||||
"diff": "^3.1.0",
|
||||
"make-error": "^1.1.1",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"source-map-support": "^0.5.3",
|
||||
"yn": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
|
@ -1077,18 +1077,18 @@
|
|||
"integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"builtin-modules": "1.1.1",
|
||||
"chalk": "2.4.1",
|
||||
"commander": "2.15.1",
|
||||
"diff": "3.5.0",
|
||||
"glob": "7.1.2",
|
||||
"js-yaml": "3.11.0",
|
||||
"minimatch": "3.0.4",
|
||||
"resolve": "1.7.1",
|
||||
"semver": "5.5.0",
|
||||
"tslib": "1.9.2",
|
||||
"tsutils": "2.27.1"
|
||||
"babel-code-frame": "^6.22.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.7.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"tslib": "^1.8.0",
|
||||
"tsutils": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
|
@ -1097,7 +1097,7 @@
|
|||
"integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "1.9.2"
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"tunnel": {
|
||||
|
@ -1110,7 +1110,7 @@
|
|||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
|
@ -1126,9 +1126,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz",
|
||||
"integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==",
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
|
||||
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
|
||||
"dev": true
|
||||
},
|
||||
"underscore": {
|
||||
|
@ -1146,9 +1146,9 @@
|
|||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "1.3.0"
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"is-buffer": "2.0.2",
|
||||
"ms-rest": "^2.3.3",
|
||||
"ms-rest-azure": "^2.5.5",
|
||||
"rhea": "^0.2.13",
|
||||
"rhea": "^0.2.14",
|
||||
"tslib": "^1.9.2",
|
||||
"uuid": "^3.2.1"
|
||||
},
|
||||
|
@ -31,14 +31,14 @@
|
|||
"mocha": "^5.2.0",
|
||||
"ts-node": "^5.0.1",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.1"
|
||||
"typescript": "^2.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"tslint": "tslint -p . -c tslint.json --exclude examples/**/*.ts --exclude tests/**/*.ts",
|
||||
"tsc": "tsc",
|
||||
"build": "npm run tslint && npm run tsc",
|
||||
"test": "npm run build",
|
||||
"unit": "mocha -r ts-node/register -t 50000 ./tests/**/*.spec.ts --abort-on-uncaught-exception",
|
||||
"unit": "nyc --reporter=lcov --reporter=text-lcov mocha -r ts-node/register -t 50000 ./tests/**/*.spec.ts --abort-on-uncaught-exception",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"homepage": "http://github.com/azure/azure-event-hubs-node",
|
||||
|
|
|
@ -25,7 +25,7 @@ describe("EventHubClient", function () {
|
|||
config[prop] = falsyVal;
|
||||
return new EventHubClient(config as any);
|
||||
};
|
||||
test.should.throw(Error, `'${prop}' is a required property of the ConnectionConfig.`);
|
||||
test.should.throw(Error, `'${prop}' is a required property of ConnectionConfig.`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as chai from "chai";
|
||||
chai.should();
|
||||
|
||||
import * as Errors from "../lib/errors";
|
||||
import * as Errors from "../lib/amqp-common/errors";
|
||||
|
||||
class AMQPError {
|
||||
name = "AmqpProtocolError";
|
||||
|
@ -21,9 +21,9 @@ describe("Errors", function () {
|
|||
describe("translate", function () {
|
||||
it("acts as a passthrough if the input is not an AmqpProtocolError", function () {
|
||||
const MyError = function () { };
|
||||
const err = new MyError();
|
||||
const err: any = new MyError();
|
||||
const msg: any = undefined;
|
||||
const ehError = new Errors.EventHubsError(msg);
|
||||
const ehError = new Errors.MessagingError(msg);
|
||||
const translatedError = Errors.translate(err);
|
||||
translatedError.name.should.equal(ehError.name);
|
||||
translatedError.retryable.should.equal(ehError.retryable);
|
||||
|
@ -31,14 +31,14 @@ describe("Errors", function () {
|
|||
});
|
||||
|
||||
[
|
||||
{ from: "amqp:not-found", to: "EventHubsCommunicationError", message: "some message" },
|
||||
{ from: "amqp:not-found", to: "ServiceCommunicationError", message: "some message" },
|
||||
{ from: "com.microsoft:server-busy", to: "ServerBusyError", message: "some message" },
|
||||
{ from: "com.microsoft:argument-out-of-range", to: "ArgumentOutOfRangeError", message: "some message" },
|
||||
{ from: "<unknown>", to: "EventHubsError" }
|
||||
{ from: "<unknown>", to: "MessagingError" }
|
||||
]
|
||||
.forEach(function (mapping) {
|
||||
it("translates " + mapping.from + " into " + mapping.to, function () {
|
||||
const err = new AMQPError(mapping.from, mapping.message);
|
||||
const err: any = new AMQPError(mapping.from, mapping.message);
|
||||
const translatedError = Errors.translate(err);
|
||||
translatedError.name.should.equal(mapping.to);
|
||||
if (translatedError.name === "ServerBusyError") {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as chai from "chai";
|
||||
chai.should();
|
||||
|
||||
import { EventData, AmqpMessage } from "../lib";
|
||||
import { EventData, Message } from "../lib";
|
||||
|
||||
const testAnnotations = {
|
||||
"x-opt-enqueued-time": Date.now(),
|
||||
|
@ -23,7 +23,7 @@ const applicationProperties = {
|
|||
propKey: "propValue"
|
||||
};
|
||||
|
||||
const testMessage: AmqpMessage = {
|
||||
const testMessage: Message = {
|
||||
body: testBody,
|
||||
message_annotations: testAnnotations,
|
||||
message_id: "test_id",
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import { retry } from "../lib/retry";
|
||||
import { retry, translate } from "../lib/amqp-common/";
|
||||
import * as chai from "chai";
|
||||
import { delay, EventHubsError } from "../lib";
|
||||
import { delay, MessagingError } from "../lib";
|
||||
import * as debugModule from "debug";
|
||||
import { translate } from "../lib/errors";
|
||||
const debug = debugModule("azure:event-hubs:retry-spec");
|
||||
const should = chai.should();
|
||||
|
||||
|
@ -42,7 +41,7 @@ describe("retry function", function () {
|
|||
await retry(operation);
|
||||
} catch (err) {
|
||||
should.exist(err);
|
||||
should.equal(true, err instanceof EventHubsError);
|
||||
should.equal(true, err instanceof MessagingError);
|
||||
err.message.should.equal("I would like to fail.");
|
||||
counter.should.equal(1);
|
||||
}
|
||||
|
@ -83,11 +82,11 @@ describe("retry function", function () {
|
|||
await delay(200);
|
||||
debug("counter: %d", ++counter);
|
||||
if (counter == 1) {
|
||||
const e = new EventHubsError("A retryable error.");
|
||||
const e = new MessagingError("A retryable error.");
|
||||
e.retryable = true;
|
||||
throw e;
|
||||
} else if (counter == 2) {
|
||||
const e = new EventHubsError("A retryable error.");
|
||||
const e = new MessagingError("A retryable error.");
|
||||
e.retryable = true;
|
||||
throw e;
|
||||
} else {
|
||||
|
@ -114,11 +113,11 @@ describe("retry function", function () {
|
|||
await delay(200);
|
||||
debug("counter: %d", ++counter);
|
||||
if (counter == 1) {
|
||||
const e = new EventHubsError("A retryable error.");
|
||||
const e = new MessagingError("A retryable error.");
|
||||
e.retryable = true;
|
||||
throw e;
|
||||
} else if (counter == 2) {
|
||||
const e = new EventHubsError("A retryable error.");
|
||||
const e = new MessagingError("A retryable error.");
|
||||
e.retryable = true;
|
||||
throw e;
|
||||
} else {
|
||||
|
@ -128,7 +127,7 @@ describe("retry function", function () {
|
|||
await retry(operation, 3, 0.5);
|
||||
} catch (err) {
|
||||
should.exist(err);
|
||||
should.equal(true, err instanceof EventHubsError);
|
||||
should.equal(true, err instanceof MessagingError);
|
||||
err.message.should.equal("I would like to fail.");
|
||||
counter.should.equal(3);
|
||||
}
|
||||
|
@ -140,14 +139,14 @@ describe("retry function", function () {
|
|||
const operation = async () => {
|
||||
debug("counter: %d", ++counter);
|
||||
await delay(200);
|
||||
const e = new EventHubsError("I would always like to fail, keep retrying.");
|
||||
const e = new MessagingError("I would always like to fail, keep retrying.");
|
||||
e.retryable = true;
|
||||
throw e;
|
||||
};
|
||||
await retry(operation, 4, 0.5);
|
||||
} catch (err) {
|
||||
should.exist(err);
|
||||
should.equal(true, err instanceof EventHubsError);
|
||||
should.equal(true, err instanceof MessagingError);
|
||||
err.message.should.equal("I would always like to fail, keep retrying.");
|
||||
counter.should.equal(4);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче