From 519246f577de43efe3ee3844174a2c72176448c7 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Fri, 12 Jun 2020 23:12:09 -0700 Subject: [PATCH 1/7] [Event Hubs] Test err scenarios against EventHubConsumerClient, not EventHubConsumer --- .../event-hubs/src/eventHubConsumerClient.ts | 3 +- .../event-hubs/src/impl/eventHubClient.ts | 18 -- .../event-hubs/src/impl/partitionGate.ts | 14 - .../test/impl/partitionGate.spec.ts | 7 - sdk/eventhub/event-hubs/test/receiver.spec.ts | 241 +++++++++--------- 5 files changed, 117 insertions(+), 166 deletions(-) diff --git a/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts b/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts index 88122fd9256e..272f645546b6 100644 --- a/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts +++ b/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts @@ -407,7 +407,6 @@ export class EventHubConsumerClient { options )); } else if ( - typeof handlersOrPartitionId1 === "string" && isSubscriptionEventHandlers(optionsOrHandlers2) ) { // #2: subscribe overload (read from specific partition IDs), don't coordinate @@ -416,7 +415,7 @@ export class EventHubConsumerClient { validateEventPositions(options.startPosition); } ({ targetedPartitionId, eventProcessor } = this.createEventProcessorForSinglePartition( - handlersOrPartitionId1, + String(handlersOrPartitionId1), optionsOrHandlers2, possibleOptions3 )); diff --git a/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts b/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts index e10c791010ff..68254b349665 100644 --- a/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts +++ b/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts @@ -334,24 +334,6 @@ export class EventHubClient { options.retryOptions = this._clientOptions.retryOptions; } throwErrorIfConnectionClosed(this._context); - throwTypeErrorIfParameterMissing( - this._context.connectionId, - "createConsumer", - "consumerGroup", - consumerGroup - ); - throwTypeErrorIfParameterMissing( - this._context.connectionId, - "createConsumer", - "partitionId", - partitionId - ); - throwTypeErrorIfParameterMissing( - this._context.connectionId, - "createConsumer", - "eventPosition", - eventPosition - ); partitionId = String(partitionId); return new EventHubConsumer(this._context, consumerGroup, partitionId, eventPosition, options); } diff --git a/sdk/eventhub/event-hubs/src/impl/partitionGate.ts b/sdk/eventhub/event-hubs/src/impl/partitionGate.ts index a8cbe305a2a8..1b45e8852e00 100644 --- a/sdk/eventhub/event-hubs/src/impl/partitionGate.ts +++ b/sdk/eventhub/event-hubs/src/impl/partitionGate.ts @@ -22,8 +22,6 @@ export class PartitionGate { * @param partitionId A partition ID or the constant "all" */ add(partitionId: string | "all") { - this._validatePartitionId(partitionId); - if ( (partitionId === "all" && this._partitions.size > 0) || this._partitions.has(partitionId) || @@ -43,16 +41,4 @@ export class PartitionGate { remove(partitionId: string | "all") { this._partitions.delete(partitionId); } - - private _validatePartitionId(partitionId: string) { - if (partitionId === "all") { - return; - } - - const partitionNumber = parseInt(partitionId, 10); - - if (isNaN(partitionNumber)) { - throw new TypeError(`Invalid partition number ${partitionId}`); - } - } } diff --git a/sdk/eventhub/event-hubs/test/impl/partitionGate.spec.ts b/sdk/eventhub/event-hubs/test/impl/partitionGate.spec.ts index 10ce6f4b5c9c..5c81ce25dab0 100644 --- a/sdk/eventhub/event-hubs/test/impl/partitionGate.spec.ts +++ b/sdk/eventhub/event-hubs/test/impl/partitionGate.spec.ts @@ -23,11 +23,4 @@ describe("PartitionGate", () => { should.throw(() => gate.add("all"), /Partition already has a subscriber/); should.throw(() => gate.add("0"), /Partition already has a subscriber/); }); - - it("invalid IDs get thrown out", () => { - const gate = new PartitionGate(); - - should.throw(() => gate.add("allo"), "Invalid partition number allo"); - should.throw(() => gate.add("woo"), "Invalid partition number woo"); - }); }); diff --git a/sdk/eventhub/event-hubs/test/receiver.spec.ts b/sdk/eventhub/event-hubs/test/receiver.spec.ts index a3b8fb589999..c7186f45e800 100644 --- a/sdk/eventhub/event-hubs/test/receiver.spec.ts +++ b/sdk/eventhub/event-hubs/test/receiver.spec.ts @@ -15,7 +15,8 @@ import { latestEventPosition, earliestEventPosition, EventHubConsumerClient, - EventHubProducerClient + EventHubProducerClient, + Subscription } from "../src"; import { EventHubClient } from "../src/impl/eventHubClient"; import { EnvVarKeys, getEnvVars } from "./utils/testUtils"; @@ -30,7 +31,8 @@ describe("EventHub Receiver", function(): void { path: env[EnvVarKeys.EVENTHUB_NAME] }; const client = new EventHubClient(service.connectionString, service.path); - const producerClient = new EventHubProducerClient(service.connectionString, service.path); + let producerClient: EventHubProducerClient; + let consumerClient: EventHubConsumerClient; let receiver: EventHubConsumer | undefined; let partitionIds: string[]; @@ -51,7 +53,20 @@ describe("EventHub Receiver", function(): void { await producerClient.close(); }); - afterEach("close the receiver link", async function(): Promise { + beforeEach(async () => { + debug("Creating the clients.."); + producerClient = new EventHubProducerClient(service.connectionString, service.path); + consumerClient = new EventHubConsumerClient( + EventHubConsumerClient.defaultConsumerGroupName, + service.connectionString, + service.path + ); + }); + + afterEach(async () => { + debug("Closing the clients.."); + await producerClient.close(); + await consumerClient.close(); if (receiver && !receiver.isClosed) { await receiver.close(); debug("After each - Receiver closed."); @@ -61,10 +76,25 @@ describe("EventHub Receiver", function(): void { describe("with partitionId 0 as number", function(): void { it("should not throw an error", async function(): Promise { - receiver = client.createConsumer(EventHubConsumerClient.defaultConsumerGroupName, 0 as any, { - sequenceNumber: 0 + let subscription: Subscription | undefined; + await new Promise((resolve, reject) => { + subscription = consumerClient.subscribe( + 0 as any, + { + processEvents: async () => { + resolve(); + }, + processError: async (err) => { + reject(err); + } + }, + { + startPosition: latestEventPosition, + maxWaitTimeInSeconds: 2 + } + ); }); - await receiver.receiveBatch(10, 20); + await subscription!.close(); }); }); @@ -574,53 +604,6 @@ describe("EventHub Receiver", function(): void { }); }); - describe("Errors when calling createConsumer", function(): void { - it("should throw an error if EventPosition is missing", function() { - try { - client.createConsumer(EventHubConsumerClient.defaultConsumerGroupName, "0", undefined as any); - throw new Error("Test failure"); - } catch (err) { - err.name.should.equal("TypeError"); - err.message.should.equal(`createConsumer called without required argument "eventPosition"`); - } - }); - - it("should throw an error if consumerGroup is missing", function() { - try { - client.createConsumer(undefined as any, "0", earliestEventPosition); - throw new Error("Test failure"); - } catch (err) { - err.name.should.equal("TypeError"); - err.message.should.equal(`createConsumer called without required argument "consumerGroup"`); - } - }); - - it("should throw MessagingEntityNotFoundError fr non existing consumer group", function(done: Mocha.Done): void { - try { - debug(">>>>>>>> client created."); - const onMessage = (data: any) => { - debug(">>>>> data: ", data); - }; - const onError = (error: any) => { - debug(">>>>>>>> error occurred", error); - // sleep for 3 seconds so that receiver link and the session can be closed properly then - // in aftereach the connection can be closed. closing the connection while the receiver - // link and it's session are being closed (and the session being removed from rhea's - // internal map) can create havoc. - setTimeout(() => { - done(should.equal(error.code, "MessagingEntityNotFoundError")); - }, 3000); - }; - receiver = client.createConsumer("some-random-name", "0", earliestEventPosition); - receiver.receive(onMessage, onError); - debug(">>>>>>>> attached the error handler on the receiver..."); - } catch (err) { - debug(">>> Some error", err); - throw new Error("This code path must not have hit.. " + JSON.stringify(err)); - } - }); - }); - describe("with trackLastEnqueuedEventProperties", function(): void { it("should have lastEnqueuedEventProperties populated", async function(): Promise { const partitionId = partitionIds[0]; @@ -891,65 +874,60 @@ describe("EventHub Receiver", function(): void { }); describe("Negative scenarios", function(): void { - describe("on invalid partition ids like", function(): void { - const invalidIds = ["XYZ", "-1", "1000", "-"]; - invalidIds.forEach(function(id: string): void { - it(`"${id}" should throw an error`, async function(): Promise { - try { - debug("Created receiver and will be receiving messages from partition id ...", id); - const d = await client - .createConsumer(EventHubConsumerClient.defaultConsumerGroupName, id, latestEventPosition) - .receiveBatch(10, 3); - debug("received messages ", d.length); - throw new Error("Test failure"); - } catch (err) { - debug("Receiver received an error", err); - should.exist(err); - err.message.should.match( - /.*The specified partition is invalid for an EventHub partition sender or receiver.*/gi - ); - } - }); + it("should throw MessagingEntityNotFoundError for non existing consumer group", async function(): Promise< + void + > { + const badConsumerClient = new EventHubConsumerClient( + "boo", + service.connectionString, + service.path + ); + let subscription: Subscription | undefined; + const caughtErr = await new Promise((resolve) => { + subscription = badConsumerClient.subscribe( + { + processEvents: async () => {}, + processError: async (err) => { + resolve(err); + } + }, + { maxWaitTimeInSeconds: 2 } + ); }); + await subscription!.close(); + await badConsumerClient.close(); - it(`" " should throw an invalid EventHub address error`, async function(): Promise { - try { - const id = " "; - debug("Created receiver and will be receiving messages from partition id ...", id); - const d = await client - .createConsumer(EventHubConsumerClient.defaultConsumerGroupName, id, latestEventPosition) - .receiveBatch(10, 3); - debug("received messages ", d.length); - throw new Error("Test failure"); - } catch (err) { - debug("Receiver received an error", err); - should.exist(err); - err.message.should.match( - /.*Invalid EventHub address. It must be either of the following.*/gi - ); - } - }); + should.exist(caughtErr); + should.equal((caughtErr as MessagingError).code, "MessagingEntityNotFoundError"); + }); - const invalidIds2 = [""]; - invalidIds2.forEach(function(id: string): void { - it(`"${id}" should throw an error`, async function(): Promise { - try { - await client - .createConsumer(EventHubConsumerClient.defaultConsumerGroupName, id, latestEventPosition) - .receiveBatch(10, 3); - throw new Error("Test failure"); - } catch (err) { - debug(`>>>> Received error - `, err); - should.exist(err); + it(`should throw an invalid EventHub address error for invalid partition`, async function(): Promise< + void + > { + let subscription: Subscription | undefined; + const caughtErr = await new Promise((resolve) => { + subscription = consumerClient.subscribe("boo", { + processEvents: async () => {}, + processError: async (err) => { + resolve(err); } }); }); + await subscription!.close(); + should.exist(caughtErr); + should.equal((caughtErr as MessagingError).code, "ArgumentOutOfRangeError"); }); it("should receive 'QuotaExceededError' when attempting to connect more than 5 receivers to a partition in a consumer group", function(done: Mocha.Done): void { const partitionId = partitionIds[0]; - const rcvHndlrs: ReceiveHandler[] = []; - const rcvrs: any[] = []; + const subscriptions: Subscription[] = []; + const clients: EventHubConsumerClient[] = []; + + const cleanup = async () => { + await Promise.all(subscriptions.map((x) => x.close())); + await Promise.all(clients.map((x) => x.close())); + console.log("cleanup done"); + }; // This test does not require recieving any messages. Just attempting to connect the 6th receiver causes // onerr2() to be called with QuotaExceededError. So it's fastest to use latestEventPosition. @@ -957,40 +935,40 @@ describe("EventHub Receiver", function(): void { // a large number of messages. const eventPosition = latestEventPosition; - debug(">>> Receivers length: ", rcvHndlrs.length); + debug(">>> Receivers length: ", subscriptions.length); for (let i = 1; i <= 5; i++) { + const innerConsumerClient = new EventHubConsumerClient( + EventHubConsumerClient.defaultConsumerGroupName, + service.connectionString, + service.path + ); const rcvrId = `rcvr-${i}`; debug(rcvrId); - const onMsg = (_data: ReceivedEventData) => { - if (!rcvrs[i]) { - rcvrs[i] = rcvrId; - debug("receiver id %s", rcvrId); - } - }; const onError = (err: MessagingError | Error) => { debug("@@@@ Error received by receiver %s", rcvrId); debug(err); }; - const rcvHndlr = client - .createConsumer(EventHubConsumerClient.defaultConsumerGroupName, partitionId, eventPosition) - .receive(onMsg, onError); - rcvHndlrs.push(rcvHndlr); + const subscription = innerConsumerClient.subscribe( + partitionId, + { + processEvents: async () => {}, + processError: async (err) => { + onError(err); + } + }, + { startPosition: eventPosition, maxWaitTimeInSeconds: 2 } + ); + subscriptions.push(subscription); + clients.push(innerConsumerClient); } debug(">>> Attached message handlers to each receiver."); setTimeout(() => { debug(`Created 6th receiver - "rcvr-6"`); - const onmsg2 = () => { - // debug(data); - }; const onerr2 = (err: MessagingError | Error) => { debug("@@@@ Error received by receiver rcvr-6"); debug(err); should.equal((err as any).code, "QuotaExceededError"); - const promises = []; - for (const rcvr of rcvHndlrs) { - promises.push(rcvr.stop()); - } - Promise.all(promises) + cleanup() .then(() => { debug("Successfully closed all the receivers.."); done(); @@ -1003,10 +981,23 @@ describe("EventHub Receiver", function(): void { done(); }); }; - const failedRcvHandler = client - .createConsumer(EventHubConsumerClient.defaultConsumerGroupName, partitionId, eventPosition) - .receive(onmsg2, onerr2); - rcvHndlrs.push(failedRcvHandler); + const failingConsumerClient = new EventHubConsumerClient( + EventHubConsumerClient.defaultConsumerGroupName, + service.connectionString, + service.path + ); + const failingSubscription = failingConsumerClient.subscribe( + partitionId, + { + processEvents: async () => {}, + processError: async (err) => { + onerr2(err); + } + }, + { startPosition: eventPosition, maxWaitTimeInSeconds: 2 } + ); + subscriptions.push(failingSubscription); + clients.push(failingConsumerClient); }, 5000); }); }); From 543deac715bdd57850c720ac0ad426ac216e5106 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 15 Jun 2020 16:40:40 -0700 Subject: [PATCH 2/7] Remove test for QuotaExceededError --- sdk/eventhub/event-hubs/test/receiver.spec.ts | 83 ------------------- 1 file changed, 83 deletions(-) diff --git a/sdk/eventhub/event-hubs/test/receiver.spec.ts b/sdk/eventhub/event-hubs/test/receiver.spec.ts index c7186f45e800..b7e49dbd65c2 100644 --- a/sdk/eventhub/event-hubs/test/receiver.spec.ts +++ b/sdk/eventhub/event-hubs/test/receiver.spec.ts @@ -917,88 +917,5 @@ describe("EventHub Receiver", function(): void { should.exist(caughtErr); should.equal((caughtErr as MessagingError).code, "ArgumentOutOfRangeError"); }); - - it("should receive 'QuotaExceededError' when attempting to connect more than 5 receivers to a partition in a consumer group", function(done: Mocha.Done): void { - const partitionId = partitionIds[0]; - const subscriptions: Subscription[] = []; - const clients: EventHubConsumerClient[] = []; - - const cleanup = async () => { - await Promise.all(subscriptions.map((x) => x.close())); - await Promise.all(clients.map((x) => x.close())); - console.log("cleanup done"); - }; - - // This test does not require recieving any messages. Just attempting to connect the 6th receiver causes - // onerr2() to be called with QuotaExceededError. So it's fastest to use latestEventPosition. - // Using EventPosition.earliestEventPosition() can cause timeouts or ServiceUnavailableException if the EventHub has - // a large number of messages. - const eventPosition = latestEventPosition; - - debug(">>> Receivers length: ", subscriptions.length); - for (let i = 1; i <= 5; i++) { - const innerConsumerClient = new EventHubConsumerClient( - EventHubConsumerClient.defaultConsumerGroupName, - service.connectionString, - service.path - ); - const rcvrId = `rcvr-${i}`; - debug(rcvrId); - const onError = (err: MessagingError | Error) => { - debug("@@@@ Error received by receiver %s", rcvrId); - debug(err); - }; - const subscription = innerConsumerClient.subscribe( - partitionId, - { - processEvents: async () => {}, - processError: async (err) => { - onError(err); - } - }, - { startPosition: eventPosition, maxWaitTimeInSeconds: 2 } - ); - subscriptions.push(subscription); - clients.push(innerConsumerClient); - } - debug(">>> Attached message handlers to each receiver."); - setTimeout(() => { - debug(`Created 6th receiver - "rcvr-6"`); - const onerr2 = (err: MessagingError | Error) => { - debug("@@@@ Error received by receiver rcvr-6"); - debug(err); - should.equal((err as any).code, "QuotaExceededError"); - cleanup() - .then(() => { - debug("Successfully closed all the receivers.."); - done(); - }) - .catch((err) => { - debug( - "An error occurred while closing the receiver in the 'QuotaExceededError' test.", - err - ); - done(); - }); - }; - const failingConsumerClient = new EventHubConsumerClient( - EventHubConsumerClient.defaultConsumerGroupName, - service.connectionString, - service.path - ); - const failingSubscription = failingConsumerClient.subscribe( - partitionId, - { - processEvents: async () => {}, - processError: async (err) => { - onerr2(err); - } - }, - { startPosition: eventPosition, maxWaitTimeInSeconds: 2 } - ); - subscriptions.push(failingSubscription); - clients.push(failingConsumerClient); - }, 5000); - }); }); }).timeout(90000); From 979937cc1cc4080f50aee87ea5dcb9c10252e33e Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 15 Jun 2020 17:01:58 -0700 Subject: [PATCH 3/7] Remove unused import resulting from master merge --- sdk/eventhub/event-hubs/src/impl/eventHubClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts b/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts index 32bab9ce9c4f..cbae69adeda9 100644 --- a/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts +++ b/sdk/eventhub/event-hubs/src/impl/eventHubClient.ts @@ -16,7 +16,7 @@ import { ConnectionContext } from "../connectionContext"; import { EventHubProperties, PartitionProperties } from "../managementClient"; import { EventPosition } from "../eventPosition"; import { EventHubConsumer } from "../receiver"; -import { throwErrorIfConnectionClosed, throwTypeErrorIfParameterMissing } from "../util/error"; +import { throwErrorIfConnectionClosed } from "../util/error"; import { EventHubClientOptions, GetEventHubPropertiesOptions, From 2583026039f53b8afb1cc4d8bc2d2035c047b033 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 16 Jun 2020 18:06:47 -0700 Subject: [PATCH 4/7] Use 0 for maxWaitTimeInSeconds because we are not waiting for any messages --- sdk/eventhub/event-hubs/test/receiver.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/eventhub/event-hubs/test/receiver.spec.ts b/sdk/eventhub/event-hubs/test/receiver.spec.ts index b7e49dbd65c2..80a6d2fa805b 100644 --- a/sdk/eventhub/event-hubs/test/receiver.spec.ts +++ b/sdk/eventhub/event-hubs/test/receiver.spec.ts @@ -90,7 +90,7 @@ describe("EventHub Receiver", function(): void { }, { startPosition: latestEventPosition, - maxWaitTimeInSeconds: 2 + maxWaitTimeInSeconds: 0 // Set timeout of 0 to resolve the promise ASAP } ); }); @@ -891,7 +891,7 @@ describe("EventHub Receiver", function(): void { resolve(err); } }, - { maxWaitTimeInSeconds: 2 } + { maxWaitTimeInSeconds: 0 } // TODO: Remove after https://github.com/Azure/azure-sdk-for-js/pull/9543 is merged ); }); await subscription!.close(); From 9692f581f91242337020d9fdafb980b0968412f5 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 16 Jun 2020 18:06:58 -0700 Subject: [PATCH 5/7] Use // @ts-expect-error instead of casting to any --- sdk/eventhub/event-hubs/test/receiver.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/eventhub/event-hubs/test/receiver.spec.ts b/sdk/eventhub/event-hubs/test/receiver.spec.ts index 80a6d2fa805b..09a237a8ac65 100644 --- a/sdk/eventhub/event-hubs/test/receiver.spec.ts +++ b/sdk/eventhub/event-hubs/test/receiver.spec.ts @@ -79,7 +79,8 @@ describe("EventHub Receiver", function(): void { let subscription: Subscription | undefined; await new Promise((resolve, reject) => { subscription = consumerClient.subscribe( - 0 as any, + // @ts-expect-error + 0, { processEvents: async () => { resolve(); From 2f443bca0bd7b96beb4206955ff140a247ddfd58 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 16 Jun 2020 18:10:48 -0700 Subject: [PATCH 6/7] Remove the debug statements in beforeEach and afterEach --- sdk/eventhub/event-hubs/test/receiver.spec.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sdk/eventhub/event-hubs/test/receiver.spec.ts b/sdk/eventhub/event-hubs/test/receiver.spec.ts index 09a237a8ac65..8380fb80e992 100644 --- a/sdk/eventhub/event-hubs/test/receiver.spec.ts +++ b/sdk/eventhub/event-hubs/test/receiver.spec.ts @@ -53,8 +53,7 @@ describe("EventHub Receiver", function(): void { await producerClient.close(); }); - beforeEach(async () => { - debug("Creating the clients.."); + beforeEach("Creating the clients", async () => { producerClient = new EventHubProducerClient(service.connectionString, service.path); consumerClient = new EventHubConsumerClient( EventHubConsumerClient.defaultConsumerGroupName, @@ -63,8 +62,7 @@ describe("EventHub Receiver", function(): void { ); }); - afterEach(async () => { - debug("Closing the clients.."); + afterEach("Closing the clients", async () => { await producerClient.close(); await consumerClient.close(); if (receiver && !receiver.isClosed) { From a0cd3358a19e50b201a5c078bae23c60970f879e Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 17 Jun 2020 11:59:04 -0700 Subject: [PATCH 7/7] Comment on casting partitionId --- sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts b/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts index 272f645546b6..4ee7f33b0503 100644 --- a/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts +++ b/sdk/eventhub/event-hubs/src/eventHubConsumerClient.ts @@ -415,6 +415,8 @@ export class EventHubConsumerClient { validateEventPositions(options.startPosition); } ({ targetedPartitionId, eventProcessor } = this.createEventProcessorForSinglePartition( + // cast to string as downstream code expects partitionId to be string, but JS users could have given us anything. + // we don't validate the user input and instead rely on service throwing errors if any String(handlersOrPartitionId1), optionsOrHandlers2, possibleOptions3