1818 */
1919#include " SchemaInfo.h"
2020#include " ProducerConfig.h"
21+ #include " Message.h"
22+ #include < future>
2123#include < map>
2224#include " pulsar/ProducerConfiguration.h"
2325
@@ -42,6 +44,8 @@ static const std::string CFG_CRYPTO_FAILURE_ACTION = "cryptoFailureAction";
4244static const std::string CFG_CHUNK_ENABLED = " chunkingEnabled" ;
4345static const std::string CFG_ACCESS_MODE = " accessMode" ;
4446static const std::string CFG_BATCHING_TYPE = " batchingType" ;
47+ static const std::string CFG_MESSAGE_ROUTER = " messageRouter" ;
48+ static const std::string CFG_MESSAGE_ROUTER_GET_PARTITION_METHOD = " getPartition" ;
4549
4650struct _pulsar_producer_configuration {
4751 pulsar::ProducerConfiguration conf;
@@ -82,6 +86,46 @@ static std::map<std::string, pulsar::ProducerConfiguration::BatchingType> PRODUC
8286 {" KeyBasedBatching" , pulsar::ProducerConfiguration::KeyBasedBatching},
8387};
8488
89+ struct MessageRouterContext {
90+ Napi::ThreadSafeFunction jsRouterFunction;
91+ };
92+
93+ static int messageRouterTrampoline (pulsar_message_t *msg, pulsar_topic_metadata_t *topicMetadata,
94+ void *ctx) {
95+ printf (" test1" );
96+ MessageRouterContext *context = static_cast <MessageRouterContext *>(ctx);
97+ int numPartitions = pulsar_topic_metadata_get_num_partitions (topicMetadata);
98+ std::promise<int > promise;
99+ std::future<int > future = promise.get_future ();
100+ auto callback = [msg, numPartitions, &promise](Napi::Env env, Napi::Function jsCallback) {
101+ printf (" test2" );
102+ Napi::Object jsMessage = Message::NewInstance (Napi::Object::New (env),
103+ std::shared_ptr<pulsar_message_t >(msg, [](pulsar_message_t *){}));
104+ Napi::Object jsTopicMetadata = Napi::Object::New (env);
105+ jsTopicMetadata.Set (" numPartitions" , Napi::Number::New (env, numPartitions));
106+ try {
107+ printf (" test3" );
108+ Napi::Value result = jsCallback.Call ({jsMessage, jsTopicMetadata});
109+ if (result.IsNumber ()) {
110+ promise.set_value (result.As <Napi::Number>().Int32Value ());
111+ } else {
112+ promise.set_value (numPartitions);
113+ }
114+ } catch (const Napi::Error& e) {
115+ fprintf (stderr, " Error in custom message router: %s\n " , e.what ());
116+ promise.set_value (numPartitions);
117+ }
118+ };
119+
120+ printf (" test3 %d" , numPartitions);
121+ napi_status status = context->jsRouterFunction .BlockingCall (callback);
122+ context->jsRouterFunction .Release ();
123+ if (status != napi_ok) {
124+ return numPartitions;
125+ }
126+ return future.get ();
127+ }
128+
85129ProducerConfig::ProducerConfig (const Napi::Object& producerConfig) : topic(" " ) {
86130 this ->cProducerConfig = std::shared_ptr<pulsar_producer_configuration_t >(
87131 pulsar_producer_configuration_create (), pulsar_producer_configuration_free);
@@ -224,6 +268,39 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
224268 if (PRODUCER_BATCHING_TYPE.count (batchingType)) {
225269 this ->cProducerConfig .get ()->conf .setBatchingType (PRODUCER_BATCHING_TYPE.at (batchingType));
226270 }
271+
272+ if (producerConfig.Has (CFG_MESSAGE_ROUTER)) {
273+ Napi::Value routerValue = producerConfig.Get (CFG_MESSAGE_ROUTER);
274+ Napi::Function routerFunc;
275+
276+ // Case 1: User passed a function directly, e.g., messageRouter: (msg, meta) => 0
277+ if (routerValue.IsFunction ()) {
278+ routerFunc = routerValue.As <Napi::Function>();
279+ }
280+ // Case 2: User passed an object, e.g., messageRouter: { getPartition: (msg, meta) => 0 }
281+ else if (routerValue.IsObject ()) {
282+ Napi::Object jsRouter = routerValue.As <Napi::Object>();
283+ if (jsRouter.Has (CFG_MESSAGE_ROUTER_GET_PARTITION_METHOD) &&
284+ jsRouter.Get (CFG_MESSAGE_ROUTER_GET_PARTITION_METHOD).IsFunction ()) {
285+ routerFunc = jsRouter.Get (CFG_MESSAGE_ROUTER_GET_PARTITION_METHOD).As <Napi::Function>();
286+ }
287+ }
288+
289+ // If we found a valid function from either case, set it up.
290+ if (routerFunc) {
291+ auto context = new MessageRouterContext ();
292+ context->jsRouterFunction =
293+ Napi::ThreadSafeFunction::New (producerConfig.Env (), routerFunc, " MessageRouterCallback" , 0 , 1 );
294+ this ->routerContext .reset (context);
295+ pulsar_producer_configuration_set_message_router (
296+ this ->cProducerConfig .get (), messageRouterTrampoline, this ->routerContext .get ());
297+ } else {
298+ Napi::TypeError::New (producerConfig.Env (), " The 'messageRouter' option must be a function, or an "
299+ " object with a 'getPartition' method." )
300+ .ThrowAsJavaScriptException ();
301+ return ;
302+ }
303+ }
227304}
228305
229306ProducerConfig::~ProducerConfig () {}
0 commit comments