@azure/event-hubs
Version:
Azure Event Hubs SDK for JS.
390 lines (389 loc) • 17 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 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