UNPKG

@azure/event-hubs

Version:
390 lines (389 loc) • 17 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 eventHubProducerClient_exports = {}; __export(eventHubProducerClient_exports, { EventHubProducerClient: () => EventHubProducerClient }); module.exports = __toCommonJS(eventHubProducerClient_exports); var import_connectionContext = require("./connectionContext.js"); var import_eventDataBatch = require("./eventDataBatch.js"); var import_core_util = require("@azure/core-util"); var import_typeGuards = require("./util/typeGuards.js"); var import_logger = require("./logger.js"); var import_error = require("./util/error.js"); var import_eventData = require("./eventData.js"); var import_eventHubSender = require("./eventHubSender.js"); var import_tracing = require("./diagnostics/tracing.js"); var import_instrumentEventData = require("./diagnostics/instrumentEventData.js"); var import_utils = require("./util/utils.js"); class EventHubProducerClient { /** * Describes the amqp connection context for the client. */ _context; /** * The options passed by the user when creating the EventHubClient instance. */ _clientOptions; /** * Map of partitionId to senders */ _sendersMap; /** * Indicates whether or not the EventHubProducerClient should enable idempotent publishing to Event Hub partitions. * If enabled, the producer will only be able to publish directly to partitions; * it will not be able to publish to the Event Hubs gateway for automatic partition routing * nor will it be able to use a partition key. * Default: false */ _enableIdempotentRetries; /** * The set of options that can be specified to influence publishing behavior specific to the configured Event Hub partition. * These options are not necessary in the majority of scenarios and are intended for use with specialized scenarios, * such as when recovering the state used for idempotent publishing. */ _partitionOptions; /** * @readonly * The name of the Event Hub instance for which this client is created. */ get eventHubName() { return this._context.config.entityPath; } /** * @readonly * The fully qualified namespace of the Event Hub instance for which this client is created. * This is likely to be similar to <yournamespace>.servicebus.windows.net. */ get fullyQualifiedNamespace() { return this._context.config.host; } /** * The name used to identify this EventHubProducerClient. * If not specified or empty, a random unique one will be generated. */ identifier; constructor(fullyQualifiedNamespaceOrConnectionString1, eventHubNameOrOptions2, credentialOrOptions3, options4) { this._context = (0, import_connectionContext.createConnectionContext)( fullyQualifiedNamespaceOrConnectionString1, eventHubNameOrOptions2, credentialOrOptions3, options4 ); if (typeof eventHubNameOrOptions2 !== "string") { this._clientOptions = eventHubNameOrOptions2 || {}; } else if (!(0, import_typeGuards.isCredential)(credentialOrOptions3)) { this._clientOptions = credentialOrOptions3 || {}; } else { this._clientOptions = options4 || {}; } this.identifier = this._clientOptions.identifier ?? (0, import_utils.getRandomName)(); this._sendersMap = /* @__PURE__ */ new Map(); } /** * Creates an instance of `EventDataBatch` to which one can add events until the maximum supported size is reached. * The batch can be passed to the {@link sendBatch} method of the `EventHubProducerClient` to be sent to Azure Event Hubs. * * Events with different values for partitionKey or partitionId will need to be put into different batches. * To simplify such batch management across partitions or to have the client automatically batch events * and send them in specific intervals, use `EventHubBufferedProducerClient` instead. * * The below example assumes you have an array of events at hand to be batched safely. * If you have events coming in one by one, `EventHubBufferedProducerClient` is recommended instead * for effecient management of batches. * * Example usage: * ```ts snippet:EventHubProducerClient_CreateBatch * import { EventHubProducerClient } from "@azure/event-hubs"; * * const client = new EventHubProducerClient("my-connection-string", "my-event-hub"); * * const messages = [ * { body: "my-event-body1" }, * { body: "my-event-body2" }, * { body: "my-event-body3" }, * { body: "my-event-body4" }, * { body: "my-event-body5" }, * ]; * * let batch = await client.createBatch(); * for (let i = 0; i < messages.length; i++) { * if (!batch.tryAdd(messages[i])) { * await client.sendBatch(batch); * batch = await client.createBatch(); * if (!batch.tryAdd(messages[i])) { * throw new Error("Message too big to fit"); * } * if (i === messages.length - 1) { * await client.sendBatch(batch); * } * } * } * ``` * * @param options - Configures the behavior of the batch. * - `partitionKey` : A value that is hashed and used by the Azure Event Hubs service to determine the partition to which * the events need to be sent. * - `partitionId` : Id of the partition to which the batch of events need to be sent. * - `maxSizeInBytes`: The upper limit for the size of batch. The `tryAdd` function will return `false` after this limit is reached. * - `abortSignal` : A signal the request to cancel the operation. * @returns Promise<EventDataBatch> * @throws Error if both `partitionId` and `partitionKey` are set in the options. * @throws Error if the underlying connection has been closed, create a new EventHubProducerClient. * @throws AbortError if the operation is cancelled via the abortSignal in the options. */ async createBatch(options = {}) { (0, import_error.throwErrorIfConnectionClosed)(this._context); const partitionId = (0, import_core_util.isDefined)(options.partitionId) ? String(options.partitionId) : void 0; (0, import_error.validateProducerPartitionSettings)({ enableIdempotentRetries: this._enableIdempotentRetries, partitionId, partitionKey: options.partitionKey }); let sender = this._sendersMap.get(partitionId || ""); if (!sender) { const partitionPublishingOptions = (0, import_core_util.isDefined)(partitionId) ? this._partitionOptions?.[partitionId] : void 0; sender = import_eventHubSender.EventHubSender.create(this._context, this.identifier, { enableIdempotentProducer: Boolean(this._enableIdempotentRetries), partitionId, partitionPublishingOptions }); this._sendersMap.set(partitionId || "", sender); } let maxMessageSize = await sender.getMaxMessageSize({ retryOptions: this._clientOptions.retryOptions, abortSignal: options.abortSignal }); if (options.maxSizeInBytes) { if (options.maxSizeInBytes > maxMessageSize) { const error = new Error( `Max message size (${options.maxSizeInBytes} bytes) is greater than maximum message size (${maxMessageSize} bytes) on the AMQP sender link.` ); import_logger.logger.warning(`[${this._context.connectionId}] ${error.message}`); (0, import_logger.logErrorStackTrace)(error); throw error; } maxMessageSize = options.maxSizeInBytes; } return new import_eventDataBatch.EventDataBatchImpl( this._context, maxMessageSize, Boolean(this._enableIdempotentRetries), options.partitionKey, partitionId ); } /** * Get the information about the state of publishing for a partition as observed by the `EventHubProducerClient`. * This data can always be read, but will only be populated with information relevant to the active features * for the producer client. * * @param partitionId - Id of the partition from which to retrieve publishing properties. * @param options - The set of options to apply to the operation call. * - `abortSignal` : A signal the request to cancel the send operation. * @returns Promise<void> * @throws AbortError if the operation is cancelled via the abortSignal. */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this is called in EventHubBufferedProducerClient via cast-to-any workaround async getPartitionPublishingProperties(partitionId, options = {}) { if (!(0, import_core_util.isDefined)(partitionId)) { throw new TypeError( `getPartitionPublishingProperties called without required argument "partitionId"` ); } if (typeof partitionId === "number") { partitionId = String(partitionId); } let sender = this._sendersMap.get(partitionId); if (!sender) { sender = import_eventHubSender.EventHubSender.create(this._context, this.identifier, { enableIdempotentProducer: Boolean(this._enableIdempotentRetries), partitionId, partitionPublishingOptions: this._partitionOptions?.[partitionId] }); this._sendersMap.set(partitionId, sender); } return sender.getPartitionPublishingProperties(options); } async sendBatch(batch, options = {}) { (0, import_error.throwErrorIfConnectionClosed)(this._context); (0, import_error.throwTypeErrorIfParameterMissing)(this._context.connectionId, "sendBatch", "batch", batch); let partitionId; let partitionKey; const eventDataTracingProperties = []; let spanContextsToLink = []; if ((0, import_eventDataBatch.isEventDataBatch)(batch)) { if (this._enableIdempotentRetries && (0, import_core_util.isDefined)(batch.startingPublishedSequenceNumber)) { throw new Error(import_error.idempotentAlreadyPublished); } const partitionAssignment = extractPartitionAssignmentFromBatch(batch, options); partitionId = partitionAssignment.partitionId; partitionKey = partitionAssignment.partitionKey; spanContextsToLink = batch._messageSpanContexts; } else { if (!Array.isArray(batch)) { batch = [batch]; } batch.forEach(import_eventData.assertIsEventData); if (batch.some((event) => (0, import_core_util.isDefined)(event._publishedSequenceNumber))) { throw new Error(import_error.idempotentSomeAlreadyPublished); } const partitionAssignment = extractPartitionAssignmentFromOptions(options); partitionId = partitionAssignment.partitionId; partitionKey = partitionAssignment.partitionKey; for (let i = 0; i < batch.length; i++) { batch[i] = (0, import_instrumentEventData.instrumentEventData)( batch[i], options, this._context.config.entityPath, this._context.config.host, "publish" ).event; eventDataTracingProperties[i] = batch[i].properties; } } (0, import_error.validateProducerPartitionSettings)({ enableIdempotentRetries: this._enableIdempotentRetries, partitionId, partitionKey }); return import_tracing.tracingClient.withSpan( `${EventHubProducerClient.name}.${this.sendBatch.name}`, options, (updatedOptions) => { let sender = this._sendersMap.get(partitionId || ""); if (!sender) { const partitionPublishingOptions = (0, import_core_util.isDefined)(partitionId) ? this._partitionOptions?.[partitionId] : void 0; sender = import_eventHubSender.EventHubSender.create(this._context, this.identifier, { enableIdempotentProducer: Boolean(this._enableIdempotentRetries), partitionId, partitionPublishingOptions }); this._sendersMap.set(partitionId || "", sender); } return sender.send(batch, { ...updatedOptions, partitionId, partitionKey, retryOptions: this._clientOptions.retryOptions }); }, { spanLinks: spanContextsToLink.map((tracingContext) => { return { tracingContext }; }), ...(0, import_tracing.toSpanOptions)(this._context.config, "publish", "client") } ); } /** * Closes the AMQP connection to the Event Hub instance, * returning a promise that will be resolved when disconnection is completed. * @returns Promise<void> * @throws Error if the underlying connection encounters an error while closing. */ async close() { await this._context.close(); for (const pair of this._sendersMap) { await pair[1].close(); } this._sendersMap.clear(); } /** * Provides the Event Hub runtime information. * @param options - The set of options to apply to the operation call. * @returns A promise that resolves with information about the Event Hub instance. * @throws Error if the underlying connection has been closed, create a new EventHubProducerClient. * @throws AbortError if the operation is cancelled via the abortSignal. */ getEventHubProperties(options = {}) { return this._context.managementSession.getEventHubProperties({ ...options, retryOptions: this._clientOptions.retryOptions }); } /** * Provides the id for each partition associated with the Event Hub. * @param options - The set of options to apply to the operation call. * @returns A promise that resolves with an Array of strings representing the id for * each partition associated with the Event Hub. * @throws Error if the underlying connection has been closed, create a new EventHubProducerClient. * @throws AbortError if the operation is cancelled via the abortSignal. */ async getPartitionIds(options = {}) { const eventHubProperties = await this._context.managementSession.getEventHubProperties({ ...options, retryOptions: this._clientOptions.retryOptions }); return eventHubProperties.partitionIds; } /** * Provides information about the state of the specified partition. * @param partitionId - The id of the partition for which information is required. * @param options - The set of options to apply to the operation call. * @returns A promise that resolves with information about the state of the partition . * @throws Error if the underlying connection has been closed, create a new EventHubProducerClient. * @throws AbortError if the operation is cancelled via the abortSignal. */ getPartitionProperties(partitionId, options = {}) { return this._context.managementSession.getPartitionProperties(partitionId, { ...options, retryOptions: this._clientOptions.retryOptions }); } } function extractPartitionAssignmentFromOptions(options = {}) { const result = {}; const { partitionId, partitionKey } = options; if ((0, import_core_util.isDefined)(partitionId)) { result.partitionId = String(partitionId); } if ((0, import_core_util.isDefined)(partitionKey)) { result.partitionKey = String(partitionKey); } return result; } function extractPartitionAssignmentFromBatch(batch, options) { const result = {}; const partitionId = batch.partitionId; const partitionKey = batch.partitionKey; const { partitionId: unexpectedPartitionId, partitionKey: unexpectedPartitionKey } = extractPartitionAssignmentFromOptions(options); if (unexpectedPartitionKey && partitionKey !== unexpectedPartitionKey) { throw new Error( `The partitionKey (${unexpectedPartitionKey}) set on sendBatch does not match the partitionKey (${partitionKey}) set when creating the batch.` ); } if (unexpectedPartitionId && unexpectedPartitionId !== partitionId) { throw new Error( `The partitionId (${unexpectedPartitionId}) set on sendBatch does not match the partitionId (${partitionId}) set when creating the batch.` ); } if ((0, import_core_util.isDefined)(partitionId)) { result.partitionId = String(partitionId); } if ((0, import_core_util.isDefined)(partitionKey)) { result.partitionKey = String(partitionKey); } return result; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { EventHubProducerClient }); //# sourceMappingURL=eventHubProducerClient.js.map