@azure/event-hubs
Version:
Azure Event Hubs SDK for JS.
370 lines (369 loc) • 14.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var managementClient_exports = {};
__export(managementClient_exports, {
ManagementClient: () => ManagementClient
});
module.exports = __toCommonJS(managementClient_exports);
var import_core_amqp = require("@azure/core-amqp");
var import_rhea_promise = require("rhea-promise");
var import_logger = require("./logger.js");
var import_error = require("./util/error.js");
var import_tracing = require("./diagnostics/tracing.js");
var import_retries = require("./util/retries.js");
var import_withAuth = require("./withAuth.js");
var import_utils = require("./util/utils.js");
class ManagementClient {
managementLock = (0, import_utils.getRandomName)(import_core_amqp.Constants.managementRequestKey);
/**
* The name/path of the entity (hub name) for which the management
* request needs to be made.
*/
entityPath;
/**
* The reply to Guid for the management client.
*/
replyTo = (0, import_utils.getRandomName)();
/**
* $management sender, receiver on the same session.
*/
_mgmtReqResLink;
/**
* The address in the following form:
* `"$management"`.
*/
address;
/**
* The token audience in the following form:
* `"sb://<your-namespace>.servicebus.windows.net/<event-hub-name>/$management"`.
*/
audience;
/**
* Provides relevant information about the amqp connection,
* cbs and $management sessions, token provider, sender and receivers.
*/
_context;
/**
* The authentication loop that keeps the token refreshed.
*/
authLoop;
logger;
/**
* Instantiates the management client.
* @param context - The connection context.
* @param address - The address for the management endpoint. For IotHub it will be
* `/messages/events/$management`.
*/
constructor(context, { address, audience } = {}) {
this.address = address ?? import_core_amqp.Constants.management;
this.audience = audience ?? context.config.getManagementAudience();
this._context = context;
const logPrefix = (0, import_logger.createManagementLogPrefix)(this._context.connectionId);
this.logger = (0, import_logger.createSimpleLogger)(import_logger.logger, logPrefix);
this.entityPath = context.config.entityPath;
}
/**
* Gets the security token for the management application properties.
* @internal
*/
async getSecurityToken() {
if ((0, import_core_amqp.isSasTokenProvider)(this._context.tokenCredential)) {
const audienceParts = this.audience.split("/");
if (audienceParts[audienceParts.length - 1] === this.address) {
audienceParts.pop();
}
const audience = audienceParts.join("/");
return this._context.tokenCredential.getToken(audience);
}
return this._context.tokenCredential.getToken(import_core_amqp.Constants.aadEventHubsScope);
}
/**
* Provides the eventhub runtime information.
*/
async getEventHubProperties(options = {}) {
(0, import_error.throwErrorIfConnectionClosed)(this._context);
return import_tracing.tracingClient.withSpan(
"ManagementClient.getEventHubProperties",
options,
async (updatedOptions) => {
try {
const securityToken = await this.getSecurityToken();
const request = {
body: Buffer.from(JSON.stringify([])),
message_id: (0, import_utils.getRandomName)(),
reply_to: this.replyTo,
application_properties: {
operation: import_core_amqp.Constants.readOperation,
name: this.entityPath,
type: `${import_core_amqp.Constants.vendorString}:${import_core_amqp.Constants.eventHub}`,
security_token: securityToken?.token
}
};
const info = await this._makeManagementRequest(request, {
...updatedOptions,
requestName: "getHubRuntimeInformation"
});
const runtimeInfo = {
name: info.name,
createdOn: new Date(info.created_at),
partitionIds: info.partition_ids,
isGeoDrEnabled: info.georeplication_factor > 1
};
import_logger.logger.verbose("the hub runtime info is: %O", runtimeInfo);
return runtimeInfo;
} catch (error) {
import_logger.logger.warning(
`an error occurred while getting the hub runtime information: ${error?.name}: ${error?.message}`
);
(0, import_logger.logErrorStackTrace)(error);
throw error;
}
},
(0, import_tracing.toSpanOptions)(this._context.config)
);
}
/**
* Provides information about the specified partition.
* @param partitionId - Partition ID for which partition information is required.
*/
async getPartitionProperties(partitionId, options = {}) {
(0, import_error.throwErrorIfConnectionClosed)(this._context);
(0, import_error.throwTypeErrorIfParameterMissing)(
this._context.connectionId,
"getPartitionProperties",
"partitionId",
partitionId
);
partitionId = String(partitionId);
return import_tracing.tracingClient.withSpan(
"ManagementClient.getPartitionProperties",
options,
async (updatedOptions) => {
try {
const securityToken = await this.getSecurityToken();
const request = {
body: Buffer.from(JSON.stringify([])),
message_id: (0, import_utils.getRandomName)(),
reply_to: this.replyTo,
application_properties: {
operation: import_core_amqp.Constants.readOperation,
name: this.entityPath,
type: `${import_core_amqp.Constants.vendorString}:${import_core_amqp.Constants.partition}`,
partition: `${partitionId}`,
security_token: securityToken?.token
}
};
const info = await this._makeManagementRequest(request, {
...updatedOptions,
requestName: "getPartitionInformation"
});
const partitionInfo = {
beginningSequenceNumber: info.begin_sequence_number,
eventHubName: info.name,
lastEnqueuedOffset: info.last_enqueued_offset,
lastEnqueuedOnUtc: new Date(info.last_enqueued_time_utc),
lastEnqueuedSequenceNumber: info.last_enqueued_sequence_number,
partitionId: info.partition,
isEmpty: info.is_partition_empty
};
import_logger.logger.verbose("the partition info is: %O.", partitionInfo);
return partitionInfo;
} catch (error) {
import_logger.logger.warning(
`an error occurred while getting the partition information: ${error?.name}: ${error?.message}`
);
(0, import_logger.logErrorStackTrace)(error);
throw error;
}
},
(0, import_tracing.toSpanOptions)(this._context.config)
);
}
/**
* Closes the AMQP management session to the Event Hub for this client,
* returning a promise that will be resolved when disconnection is completed.
*/
async close() {
try {
this.authLoop?.stop();
if (this._isMgmtRequestResponseLinkOpen()) {
const mgmtLink = this._mgmtReqResLink;
this._mgmtReqResLink = void 0;
await mgmtLink.close();
import_logger.logger.info("successfully closed the management session.");
}
} catch (err) {
const msg = `an error occurred while closing the management session: ${err?.name}: ${err?.message}`;
import_logger.logger.warning(msg);
(0, import_logger.logErrorStackTrace)(err);
throw new Error(msg);
}
}
async _init({
abortSignal,
timeoutInMs
}) {
const createLink = async () => {
const rxopt = {
source: { address: this.address },
name: this.replyTo,
target: { address: this.replyTo },
onSessionError: (context) => {
const ehError = (0, import_core_amqp.translate)(context.session.error);
import_logger.logger.verbose(
"an error occurred on the session for request/response links for $management: %O",
ehError
);
}
};
const sropt = {
target: { address: this.address }
};
import_logger.logger.verbose(
"creating sender/receiver links with srOpts: %o, receiverOpts: %O.",
sropt,
rxopt
);
this._mgmtReqResLink = await import_core_amqp.RequestResponseLink.create(
this._context.connection,
sropt,
rxopt,
{ abortSignal }
);
this._mgmtReqResLink.sender.on(import_rhea_promise.SenderEvents.senderError, (context) => {
const ehError = (0, import_core_amqp.translate)(context.sender.error);
import_logger.logger.verbose("an error occurred on the $management sender link.. %O", ehError);
});
this._mgmtReqResLink.receiver.on(import_rhea_promise.ReceiverEvents.receiverError, (context) => {
const ehError = (0, import_core_amqp.translate)(context.receiver.error);
import_logger.logger.verbose("an error occurred on the $management receiver link.. %O", ehError);
});
import_logger.logger.verbose(
"created sender '%s' and receiver '%s' links",
this._mgmtReqResLink.sender.name,
this._mgmtReqResLink.receiver.name
);
};
try {
if (!this._isMgmtRequestResponseLinkOpen()) {
await this._context.readyToOpenLink();
this.authLoop = await (0, import_withAuth.withAuth)(
createLink,
this._context,
this.audience,
timeoutInMs,
this.logger,
{ abortSignal }
);
}
} catch (err) {
const translatedError = (0, import_core_amqp.translate)(err);
import_logger.logger.warning(
`an error occurred while establishing the links: ${translatedError?.name}: ${translatedError?.message}`
);
(0, import_logger.logErrorStackTrace)(translatedError);
throw translatedError;
}
}
/**
* Helper method to make the management request
* @param request - The AMQP message to send
* @param options - The options to use when sending a request over a $management link
*/
async _makeManagementRequest(request, options = {}) {
const retryOptions = options.retryOptions || {};
try {
const abortSignal = options && options.abortSignal;
const sendOperationPromise = async () => {
let count = 0;
const retryTimeoutInMs = (0, import_retries.getRetryAttemptTimeoutInMs)(options.retryOptions);
let timeTakenByInit = 0;
if (!this._isMgmtRequestResponseLinkOpen()) {
import_logger.logger.verbose("acquiring lock to get the management req res link.");
const initOperationStartTime = Date.now();
try {
await import_core_amqp.defaultCancellableLock.acquire(
this.managementLock,
() => {
const acquireLockEndTime = Date.now();
const timeoutInMs = retryTimeoutInMs - (acquireLockEndTime - initOperationStartTime);
return this._init({ abortSignal, timeoutInMs });
},
{ abortSignal, timeoutInMs: retryTimeoutInMs }
);
} catch (err) {
const translatedError = (0, import_core_amqp.translate)(err);
import_logger.logger.warning(
"an error occurred while creating the link: %s",
`${translatedError?.name}: ${translatedError?.message}`
);
(0, import_logger.logErrorStackTrace)(translatedError);
throw translatedError;
}
timeTakenByInit = Date.now() - initOperationStartTime;
}
const remainingOperationTimeoutInMs = retryTimeoutInMs - timeTakenByInit;
const sendRequestOptions = {
abortSignal: options.abortSignal,
requestName: options.requestName,
timeoutInMs: remainingOperationTimeoutInMs
};
count++;
if (count !== 1) {
request.message_id = (0, import_utils.getRandomName)();
} else if (!request.message_id) {
request.message_id = (0, import_utils.getRandomName)();
}
return this._mgmtReqResLink.sendRequest(request, sendRequestOptions);
};
const config = Object.defineProperties(
{
operation: sendOperationPromise,
operationType: import_core_amqp.RetryOperationType.management,
abortSignal,
retryOptions
},
{
connectionId: {
enumerable: true,
get: () => {
return this._context.connectionId;
}
}
}
);
return (await (0, import_core_amqp.retry)(config)).body;
} catch (err) {
const translatedError = (0, import_core_amqp.translate)(err);
import_logger.logger.warning(
"an error occurred during send on management request-response link with address: %s",
`${translatedError?.name}: ${translatedError?.message}`
);
(0, import_logger.logErrorStackTrace)(translatedError);
throw translatedError;
}
}
_isMgmtRequestResponseLinkOpen() {
return this._mgmtReqResLink && this._mgmtReqResLink.isOpen();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ManagementClient
});
//# sourceMappingURL=managementClient.js.map