UNPKG

@azure/event-hubs

Version:
370 lines (369 loc) • 14.2 kB
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