pulsar-client 1.6.2 → 1.8.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.asf.yaml +8 -1
  2. package/.github/PULL_REQUEST_TEMPLATE.md +85 -0
  3. package/.github/workflows/ci-build-release-napi.yml +213 -0
  4. package/.github/workflows/ci-pr-validation.yml +225 -0
  5. package/README.md +87 -68
  6. package/binding.gyp +41 -39
  7. package/{pulsar-test-service-stop.sh → build-support/dep-version.py} +4 -6
  8. package/build-support/download-release-artifacts.py +74 -0
  9. package/build-support/generate-source-archive.sh +28 -0
  10. package/build-support/install-cpp-client.sh +66 -0
  11. package/{pulsar-test-service-start.sh → build-support/pulsar-test-container-start.sh} +11 -21
  12. package/build-support/pulsar-test-service-start.sh +37 -0
  13. package/build-support/pulsar-test-service-stop.sh +32 -0
  14. package/{.github/workflows/nodejs.yml → build-support/sign-files.sh} +13 -12
  15. package/build-support/stage-release.sh +44 -0
  16. package/dependencies.yaml +28 -0
  17. package/docs/release-process.md +262 -0
  18. package/examples/consumer.js +1 -1
  19. package/examples/consumer_listener.js +1 -1
  20. package/examples/consumer_tls_auth.js +1 -1
  21. package/examples/custom_logger.js +60 -0
  22. package/examples/encryption-consumer.js +1 -1
  23. package/examples/encryption-producer.js +1 -1
  24. package/examples/producer.js +1 -1
  25. package/examples/producer_tls_auth.js +1 -1
  26. package/examples/reader.js +1 -1
  27. package/examples/reader_listener.js +1 -1
  28. package/index.d.ts +12 -4
  29. package/index.js +2 -1
  30. package/package.json +16 -13
  31. package/pkg/build-napi-inside-docker.sh +31 -0
  32. package/pkg/linux_glibc/Dockerfile +33 -0
  33. package/pkg/linux_musl/Dockerfile +32 -0
  34. package/pkg/load_test.js +30 -0
  35. package/pkg/mac/build-cpp-deps-lib.sh +186 -0
  36. package/pkg/mac/build-cpp-lib.sh +51 -0
  37. package/{docker-tests.sh → pkg/mac/common.sh} +13 -13
  38. package/pkg/windows/download-cpp-client.bat +12 -0
  39. package/pulsar-client-cpp.txt +2 -0
  40. package/src/AuthenticationAthenz.js +1 -1
  41. package/src/AuthenticationOauth2.js +1 -1
  42. package/src/AuthenticationTls.js +1 -1
  43. package/src/AuthenticationToken.js +1 -1
  44. package/src/Client.cc +84 -58
  45. package/src/Client.h +6 -4
  46. package/src/Consumer.cc +331 -234
  47. package/src/Consumer.h +11 -9
  48. package/src/ConsumerConfig.cc +54 -32
  49. package/src/ConsumerConfig.h +5 -6
  50. package/src/Message.cc +26 -29
  51. package/src/Message.h +4 -4
  52. package/src/MessageId.cc +19 -22
  53. package/src/MessageId.h +5 -6
  54. package/src/MessageListener.h +3 -8
  55. package/src/Producer.cc +116 -133
  56. package/src/Producer.h +3 -3
  57. package/src/ProducerConfig.cc +39 -22
  58. package/src/ProducerConfig.h +2 -2
  59. package/src/Reader.cc +147 -128
  60. package/src/Reader.h +5 -3
  61. package/src/ReaderConfig.cc +14 -20
  62. package/src/ReaderConfig.h +5 -6
  63. package/src/ReaderListener.h +2 -7
  64. package/src/SchemaInfo.cc +78 -0
  65. package/src/SchemaInfo.h +41 -0
  66. package/src/ThreadSafeDeferred.cc +98 -0
  67. package/src/ThreadSafeDeferred.h +85 -0
  68. package/src/pulsar-binding.js +26 -0
  69. package/tests/conf/standalone.conf +6 -0
  70. package/tests/consumer.test.js +2 -2
  71. package/tests/end_to_end.test.js +214 -2
  72. package/tests/producer.test.js +2 -2
  73. package/{run-unit-tests.sh → tests/run-unit-tests.sh} +5 -14
  74. package/pulsar-version.txt +0 -1
package/src/Consumer.cc CHANGED
@@ -21,6 +21,7 @@
21
21
  #include "ConsumerConfig.h"
22
22
  #include "Message.h"
23
23
  #include "MessageId.h"
24
+ #include "ThreadSafeDeferred.h"
24
25
  #include <pulsar/c/result.h>
25
26
  #include <atomic>
26
27
  #include <thread>
@@ -40,6 +41,8 @@ void Consumer::Init(Napi::Env env, Napi::Object exports) {
40
41
  InstanceMethod("negativeAcknowledgeId", &Consumer::NegativeAcknowledgeId),
41
42
  InstanceMethod("acknowledgeCumulative", &Consumer::AcknowledgeCumulative),
42
43
  InstanceMethod("acknowledgeCumulativeId", &Consumer::AcknowledgeCumulativeId),
44
+ InstanceMethod("seek", &Consumer::Seek),
45
+ InstanceMethod("seekTimestamp", &Consumer::SeekTimestamp),
43
46
  InstanceMethod("isConnected", &Consumer::IsConnected),
44
47
  InstanceMethod("close", &Consumer::Close),
45
48
  InstanceMethod("unsubscribe", &Consumer::Unsubscribe),
@@ -50,10 +53,10 @@ void Consumer::Init(Napi::Env env, Napi::Object exports) {
50
53
  }
51
54
 
52
55
  struct MessageListenerProxyData {
53
- pulsar_message_t *cMessage;
56
+ std::shared_ptr<pulsar_message_t> cMessage;
54
57
  Consumer *consumer;
55
58
 
56
- MessageListenerProxyData(pulsar_message_t *cMessage, Consumer *consumer)
59
+ MessageListenerProxyData(std::shared_ptr<pulsar_message_t> cMessage, Consumer *consumer)
57
60
  : cMessage(cMessage), consumer(consumer) {}
58
61
  };
59
62
 
@@ -62,11 +65,16 @@ void MessageListenerProxy(Napi::Env env, Napi::Function jsCallback, MessageListe
62
65
  Consumer *consumer = data->consumer;
63
66
  delete data;
64
67
 
65
- jsCallback.Call({msg, consumer->Value()});
68
+ // `consumer` might be null in certain cases, segmentation fault might happend without this null check. We
69
+ // need to handle this rare case in future.
70
+ if (consumer) {
71
+ jsCallback.Call({msg, consumer->Value()});
72
+ }
66
73
  }
67
74
 
68
- void MessageListener(pulsar_consumer_t *cConsumer, pulsar_message_t *cMessage, void *ctx) {
69
- ListenerCallback *listenerCallback = (ListenerCallback *)ctx;
75
+ void MessageListener(pulsar_consumer_t *rawConsumer, pulsar_message_t *rawMessage, void *ctx) {
76
+ std::shared_ptr<pulsar_message_t> cMessage(rawMessage, pulsar_message_free);
77
+ MessageListenerCallback *listenerCallback = (MessageListenerCallback *)ctx;
70
78
 
71
79
  Consumer *consumer = (Consumer *)listenerCallback->consumer;
72
80
 
@@ -79,144 +87,131 @@ void MessageListener(pulsar_consumer_t *cConsumer, pulsar_message_t *cMessage, v
79
87
  listenerCallback->callback.Release();
80
88
  }
81
89
 
82
- void Consumer::SetCConsumer(std::shared_ptr<CConsumerWrapper> cConsumer) { this->wrapper = cConsumer; }
83
- void Consumer::SetListenerCallback(ListenerCallback *listener) {
84
- if (listener) {
85
- // Maintain reference to consumer, so it won't get garbage collected
86
- // since, when we have a listener, we don't have to maintain reference to consumer (in js code)
87
- this->Ref();
90
+ void Consumer::SetCConsumer(std::shared_ptr<pulsar_consumer_t> cConsumer) { this->cConsumer = cConsumer; }
91
+ void Consumer::SetListenerCallback(MessageListenerCallback *listener) {
92
+ if (this->listener != nullptr) {
93
+ // It is only safe to set the listener once for the lifecycle of the Consumer
94
+ return;
95
+ }
88
96
 
89
- // Pass consumer as argument
97
+ if (listener != nullptr) {
90
98
  listener->consumer = this;
99
+ // If a consumer listener is set, the Consumer instance is kept alive even if it goes out of scope in JS
100
+ // code.
101
+ this->Ref();
102
+ this->listener = listener;
91
103
  }
92
-
93
- this->listener = listener;
94
104
  }
95
105
 
96
106
  Consumer::Consumer(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Consumer>(info), listener(nullptr) {}
97
107
 
98
- class ConsumerNewInstanceWorker : public Napi::AsyncWorker {
99
- public:
100
- ConsumerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
101
- ConsumerConfig *consumerConfig, std::shared_ptr<CConsumerWrapper> consumerWrapper)
102
- : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
103
- deferred(deferred),
104
- cClient(cClient),
105
- consumerConfig(consumerConfig),
106
- consumerWrapper(consumerWrapper) {}
107
- ~ConsumerNewInstanceWorker() {}
108
- void Execute() {
109
- const std::string &topic = this->consumerConfig->GetTopic();
110
- const std::vector<std::string> &topics = this->consumerConfig->GetTopics();
111
- const std::string &topicsPattern = this->consumerConfig->GetTopicsPattern();
112
- if (topic.empty() && topics.size() == 0 && topicsPattern.empty()) {
113
- SetError(
114
- std::string("Topic, topics or topicsPattern is required and must be specified as a string when "
115
- "creating consumer"));
116
- return;
117
- }
118
- const std::string &subscription = this->consumerConfig->GetSubscription();
119
- if (subscription.empty()) {
120
- SetError(
121
- std::string("Subscription is required and must be specified as a string when creating consumer"));
122
- return;
123
- }
124
- int32_t ackTimeoutMs = this->consumerConfig->GetAckTimeoutMs();
125
- if (ackTimeoutMs != 0 && ackTimeoutMs < MIN_ACK_TIMEOUT_MILLIS) {
126
- std::string msg("Ack timeout should be 0 or greater than or equal to " +
127
- std::to_string(MIN_ACK_TIMEOUT_MILLIS));
128
- SetError(msg);
129
- return;
130
- }
131
- int32_t nAckRedeliverTimeoutMs = this->consumerConfig->GetNAckRedeliverTimeoutMs();
132
- if (nAckRedeliverTimeoutMs < 0) {
133
- std::string msg("NAck timeout should be greater than or equal to zero");
134
- SetError(msg);
135
- return;
136
- }
108
+ struct ConsumerNewInstanceContext {
109
+ ConsumerNewInstanceContext(std::shared_ptr<ThreadSafeDeferred> deferred,
110
+ std::shared_ptr<pulsar_client_t> cClient,
111
+ std::shared_ptr<ConsumerConfig> consumerConfig)
112
+ : deferred(deferred), cClient(cClient), consumerConfig(consumerConfig){};
113
+ std::shared_ptr<ThreadSafeDeferred> deferred;
114
+ std::shared_ptr<pulsar_client_t> cClient;
115
+ std::shared_ptr<ConsumerConfig> consumerConfig;
116
+
117
+ static void subscribeCallback(pulsar_result result, pulsar_consumer_t *rawConsumer, void *ctx) {
118
+ auto instanceContext = static_cast<ConsumerNewInstanceContext *>(ctx);
119
+ auto deferred = instanceContext->deferred;
120
+ auto cClient = instanceContext->cClient;
121
+ auto consumerConfig = instanceContext->consumerConfig;
122
+ delete instanceContext;
137
123
 
138
- this->done = false;
139
- if (!topicsPattern.empty()) {
140
- pulsar_client_subscribe_pattern_async(this->cClient, topicsPattern.c_str(), subscription.c_str(),
141
- this->consumerConfig->GetCConsumerConfig(),
142
- &ConsumerNewInstanceWorker::subscribeCallback, (void *)this);
143
- } else if (topics.size() > 0) {
144
- const char **cTopics = new const char *[topics.size()];
145
- for (size_t i = 0; i < topics.size(); i++) {
146
- cTopics[i] = topics[i].c_str();
147
- }
148
- pulsar_client_subscribe_multi_topics_async(this->cClient, cTopics, topics.size(), subscription.c_str(),
149
- this->consumerConfig->GetCConsumerConfig(),
150
- &ConsumerNewInstanceWorker::subscribeCallback, (void *)this);
151
- delete cTopics;
152
- } else {
153
- pulsar_client_subscribe_async(this->cClient, topic.c_str(), subscription.c_str(),
154
- this->consumerConfig->GetCConsumerConfig(),
155
- &ConsumerNewInstanceWorker::subscribeCallback, (void *)this);
156
- }
157
- while (!done) {
158
- std::this_thread::yield();
124
+ if (result != pulsar_result_Ok) {
125
+ return deferred->Reject(std::string("Failed to create consumer: ") + pulsar_result_str(result));
159
126
  }
160
- }
161
- void OnOK() {
162
- Napi::Object obj = Consumer::constructor.New({});
163
- Consumer *consumer = Consumer::Unwrap(obj);
164
127
 
165
- consumer->SetCConsumer(this->consumerWrapper);
166
- consumer->SetListenerCallback(this->listener);
128
+ auto cConsumer = std::shared_ptr<pulsar_consumer_t>(rawConsumer, pulsar_consumer_free);
129
+ auto listener = consumerConfig->GetListenerCallback();
167
130
 
168
- if (this->listener) {
169
- // resume to enable MessageListener function callback
170
- resume_message_listener(this->consumerWrapper->cConsumer);
131
+ if (listener) {
132
+ // pause, will resume in OnOK, to prevent MessageListener get a nullptr of consumer
133
+ pulsar_consumer_pause_message_listener(cConsumer.get());
171
134
  }
172
135
 
173
- this->deferred.Resolve(obj);
174
- }
175
- void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
136
+ deferred->Resolve([cConsumer, consumerConfig, listener](const Napi::Env env) {
137
+ Napi::Object obj = Consumer::constructor.New({});
138
+ Consumer *consumer = Consumer::Unwrap(obj);
176
139
 
177
- private:
178
- Napi::Promise::Deferred deferred;
179
- pulsar_client_t *cClient;
180
- pulsar_consumer_t *cConsumer;
181
- ConsumerConfig *consumerConfig;
182
- ListenerCallback *listener;
183
- std::shared_ptr<CConsumerWrapper> consumerWrapper;
184
- std::atomic<bool> done;
185
- static void subscribeCallback(pulsar_result result, pulsar_consumer_t *consumer, void *ctx) {
186
- ConsumerNewInstanceWorker *worker = (ConsumerNewInstanceWorker *)ctx;
187
- if (result != pulsar_result_Ok) {
188
- worker->SetError(std::string("Failed to create consumer: ") + pulsar_result_str(result));
189
- } else {
190
- worker->consumerWrapper->cConsumer = consumer;
191
- worker->listener = worker->consumerConfig->GetListenerCallback();
140
+ consumer->SetCConsumer(cConsumer);
141
+ consumer->SetListenerCallback(listener);
192
142
 
193
- if (worker->listener) {
194
- // pause, will resume in OnOK, to prevent MessageListener get a nullptr of consumer
195
- pulsar_consumer_pause_message_listener(consumer);
143
+ if (listener) {
144
+ // resume to enable MessageListener function callback
145
+ resume_message_listener(cConsumer.get());
196
146
  }
197
- }
198
147
 
199
- delete worker->consumerConfig;
200
- worker->done = true;
148
+ return obj;
149
+ });
201
150
  }
202
151
  };
203
152
 
204
- Napi::Value Consumer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
205
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
206
- Napi::Object config = info[0].As<Napi::Object>();
153
+ Napi::Value Consumer::NewInstance(const Napi::CallbackInfo &info, std::shared_ptr<pulsar_client_t> cClient) {
154
+ auto deferred = ThreadSafeDeferred::New(info.Env());
155
+ auto config = info[0].As<Napi::Object>();
156
+ std::shared_ptr<ConsumerConfig> consumerConfig = std::make_shared<ConsumerConfig>(config, &MessageListener);
157
+
158
+ const std::string &topic = consumerConfig->GetTopic();
159
+ const std::vector<std::string> &topics = consumerConfig->GetTopics();
160
+ const std::string &topicsPattern = consumerConfig->GetTopicsPattern();
161
+ if (topic.empty() && topics.size() == 0 && topicsPattern.empty()) {
162
+ deferred->Reject(
163
+ std::string("Topic, topics or topicsPattern is required and must be specified as a string when "
164
+ "creating consumer"));
165
+ return deferred->Promise();
166
+ }
167
+ const std::string &subscription = consumerConfig->GetSubscription();
168
+ if (subscription.empty()) {
169
+ deferred->Reject(
170
+ std::string("Subscription is required and must be specified as a string when creating consumer"));
171
+ return deferred->Promise();
172
+ }
173
+ int32_t ackTimeoutMs = consumerConfig->GetAckTimeoutMs();
174
+ if (ackTimeoutMs != 0 && ackTimeoutMs < MIN_ACK_TIMEOUT_MILLIS) {
175
+ std::string msg("Ack timeout should be 0 or greater than or equal to " +
176
+ std::to_string(MIN_ACK_TIMEOUT_MILLIS));
177
+ deferred->Reject(msg);
178
+ return deferred->Promise();
179
+ }
180
+ int32_t nAckRedeliverTimeoutMs = consumerConfig->GetNAckRedeliverTimeoutMs();
181
+ if (nAckRedeliverTimeoutMs < 0) {
182
+ std::string msg("NAck timeout should be greater than or equal to zero");
183
+ deferred->Reject(msg);
184
+ return deferred->Promise();
185
+ }
207
186
 
208
- std::shared_ptr<CConsumerWrapper> consumerWrapper = std::make_shared<CConsumerWrapper>();
187
+ auto ctx = new ConsumerNewInstanceContext(deferred, cClient, consumerConfig);
188
+
189
+ if (!topicsPattern.empty()) {
190
+ pulsar_client_subscribe_pattern_async(cClient.get(), topicsPattern.c_str(), subscription.c_str(),
191
+ consumerConfig->GetCConsumerConfig().get(),
192
+ &ConsumerNewInstanceContext::subscribeCallback, ctx);
193
+ } else if (topics.size() > 0) {
194
+ const char **cTopics = new const char *[topics.size()];
195
+ for (size_t i = 0; i < topics.size(); i++) {
196
+ cTopics[i] = topics[i].c_str();
197
+ }
198
+ pulsar_client_subscribe_multi_topics_async(cClient.get(), cTopics, topics.size(), subscription.c_str(),
199
+ consumerConfig->GetCConsumerConfig().get(),
200
+ &ConsumerNewInstanceContext::subscribeCallback, ctx);
201
+ delete[] cTopics;
202
+ } else {
203
+ pulsar_client_subscribe_async(cClient.get(), topic.c_str(), subscription.c_str(),
204
+ consumerConfig->GetCConsumerConfig().get(),
205
+ &ConsumerNewInstanceContext::subscribeCallback, ctx);
206
+ }
209
207
 
210
- ConsumerConfig *consumerConfig = new ConsumerConfig(config, consumerWrapper, &MessageListener);
211
- ConsumerNewInstanceWorker *wk =
212
- new ConsumerNewInstanceWorker(deferred, cClient, consumerConfig, consumerWrapper);
213
- wk->Queue();
214
- return deferred.Promise();
208
+ return deferred->Promise();
215
209
  }
216
210
 
211
+ // We still need a receive worker because the c api is missing the equivalent async definition
217
212
  class ConsumerReceiveWorker : public Napi::AsyncWorker {
218
213
  public:
219
- ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer,
214
+ ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, std::shared_ptr<pulsar_consumer_t> cConsumer,
220
215
  int64_t timeout = -1)
221
216
  : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
222
217
  deferred(deferred),
@@ -225,14 +220,17 @@ class ConsumerReceiveWorker : public Napi::AsyncWorker {
225
220
  ~ConsumerReceiveWorker() {}
226
221
  void Execute() {
227
222
  pulsar_result result;
223
+ pulsar_message_t *rawMessage;
228
224
  if (timeout > 0) {
229
- result = pulsar_consumer_receive_with_timeout(this->cConsumer, &(this->cMessage), timeout);
225
+ result = pulsar_consumer_receive_with_timeout(this->cConsumer.get(), &rawMessage, timeout);
230
226
  } else {
231
- result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
227
+ result = pulsar_consumer_receive(this->cConsumer.get(), &rawMessage);
232
228
  }
233
229
 
234
230
  if (result != pulsar_result_Ok) {
235
- SetError(std::string("Failed to received message ") + pulsar_result_str(result));
231
+ SetError(std::string("Failed to receive message: ") + pulsar_result_str(result));
232
+ } else {
233
+ this->cMessage = std::shared_ptr<pulsar_message_t>(rawMessage, pulsar_message_free);
236
234
  }
237
235
  }
238
236
  void OnOK() {
@@ -243,160 +241,259 @@ class ConsumerReceiveWorker : public Napi::AsyncWorker {
243
241
 
244
242
  private:
245
243
  Napi::Promise::Deferred deferred;
246
- pulsar_consumer_t *cConsumer;
247
- pulsar_message_t *cMessage;
244
+ std::shared_ptr<pulsar_consumer_t> cConsumer;
245
+ std::shared_ptr<pulsar_message_t> cMessage;
248
246
  int64_t timeout;
249
247
  };
250
248
 
251
249
  Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
252
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
253
250
  if (info[0].IsUndefined()) {
254
- ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->wrapper->cConsumer);
255
- wk->Queue();
251
+ auto deferred = ThreadSafeDeferred::New(Env());
252
+ auto ctx = new ExtDeferredContext(deferred);
253
+ pulsar_consumer_receive_async(
254
+ this->cConsumer.get(),
255
+ [](pulsar_result result, pulsar_message_t *rawMessage, void *ctx) {
256
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
257
+ auto deferred = deferredContext->deferred;
258
+ delete deferredContext;
259
+
260
+ if (result != pulsar_result_Ok) {
261
+ deferred->Reject(std::string("Failed to receive message: ") + pulsar_result_str(result));
262
+ } else {
263
+ deferred->Resolve([rawMessage](const Napi::Env env) {
264
+ Napi::Object obj = Message::NewInstance(
265
+ {}, std::shared_ptr<pulsar_message_t>(rawMessage, pulsar_message_free));
266
+ return obj;
267
+ });
268
+ }
269
+ },
270
+ ctx);
271
+ return deferred->Promise();
256
272
  } else {
273
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
257
274
  Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
258
- ConsumerReceiveWorker *wk =
259
- new ConsumerReceiveWorker(deferred, this->wrapper->cConsumer, timeout.Int64Value());
275
+ ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer, timeout.Int64Value());
260
276
  wk->Queue();
277
+ return deferred.Promise();
261
278
  }
262
- return deferred.Promise();
263
279
  }
264
280
 
265
- void Consumer::Acknowledge(const Napi::CallbackInfo &info) {
266
- Napi::Object obj = info[0].As<Napi::Object>();
267
- Message *msg = Message::Unwrap(obj);
268
- pulsar_consumer_acknowledge_async(this->wrapper->cConsumer, msg->GetCMessage(), NULL, NULL);
281
+ Napi::Value Consumer::Acknowledge(const Napi::CallbackInfo &info) {
282
+ auto obj = info[0].As<Napi::Object>();
283
+ auto msg = Message::Unwrap(obj);
284
+ auto deferred = ThreadSafeDeferred::New(Env());
285
+ auto ctx = new ExtDeferredContext(deferred);
286
+
287
+ pulsar_consumer_acknowledge_async(
288
+ this->cConsumer.get(), msg->GetCMessage().get(),
289
+ [](pulsar_result result, void *ctx) {
290
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
291
+ auto deferred = deferredContext->deferred;
292
+ delete deferredContext;
293
+
294
+ if (result != pulsar_result_Ok) {
295
+ deferred->Reject(std::string("Failed to acknowledge: ") + pulsar_result_str(result));
296
+ } else {
297
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
298
+ }
299
+ },
300
+ ctx);
301
+
302
+ return deferred->Promise();
269
303
  }
270
304
 
271
- void Consumer::AcknowledgeId(const Napi::CallbackInfo &info) {
272
- Napi::Object obj = info[0].As<Napi::Object>();
273
- MessageId *msgId = MessageId::Unwrap(obj);
274
- pulsar_consumer_acknowledge_async_id(this->wrapper->cConsumer, msgId->GetCMessageId(), NULL, NULL);
305
+ Napi::Value Consumer::AcknowledgeId(const Napi::CallbackInfo &info) {
306
+ auto obj = info[0].As<Napi::Object>();
307
+ auto *msgId = MessageId::Unwrap(obj);
308
+ auto deferred = ThreadSafeDeferred::New(Env());
309
+ auto ctx = new ExtDeferredContext(deferred);
310
+
311
+ pulsar_consumer_acknowledge_async_id(
312
+ this->cConsumer.get(), msgId->GetCMessageId().get(),
313
+ [](pulsar_result result, void *ctx) {
314
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
315
+ auto deferred = deferredContext->deferred;
316
+ delete deferredContext;
317
+
318
+ if (result != pulsar_result_Ok) {
319
+ deferred->Reject(std::string("Failed to acknowledge id: ") + pulsar_result_str(result));
320
+ } else {
321
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
322
+ }
323
+ },
324
+ ctx);
325
+
326
+ return deferred->Promise();
275
327
  }
276
328
 
277
329
  void Consumer::NegativeAcknowledge(const Napi::CallbackInfo &info) {
278
330
  Napi::Object obj = info[0].As<Napi::Object>();
279
331
  Message *msg = Message::Unwrap(obj);
280
- pulsar_consumer_negative_acknowledge(this->wrapper->cConsumer, msg->GetCMessage());
332
+ std::shared_ptr<pulsar_message_t> cMessage = msg->GetCMessage();
333
+ pulsar_consumer_negative_acknowledge(this->cConsumer.get(), cMessage.get());
281
334
  }
282
335
 
283
336
  void Consumer::NegativeAcknowledgeId(const Napi::CallbackInfo &info) {
284
337
  Napi::Object obj = info[0].As<Napi::Object>();
285
338
  MessageId *msgId = MessageId::Unwrap(obj);
286
- pulsar_consumer_negative_acknowledge_id(this->wrapper->cConsumer, msgId->GetCMessageId());
339
+ std::shared_ptr<pulsar_message_id_t> cMessageId = msgId->GetCMessageId();
340
+ pulsar_consumer_negative_acknowledge_id(this->cConsumer.get(), cMessageId.get());
287
341
  }
288
342
 
289
- void Consumer::AcknowledgeCumulative(const Napi::CallbackInfo &info) {
290
- Napi::Object obj = info[0].As<Napi::Object>();
291
- Message *msg = Message::Unwrap(obj);
292
- pulsar_consumer_acknowledge_cumulative_async(this->wrapper->cConsumer, msg->GetCMessage(), NULL, NULL);
343
+ Napi::Value Consumer::AcknowledgeCumulative(const Napi::CallbackInfo &info) {
344
+ auto obj = info[0].As<Napi::Object>();
345
+ auto *msg = Message::Unwrap(obj);
346
+ auto deferred = ThreadSafeDeferred::New(Env());
347
+ auto ctx = new ExtDeferredContext(deferred);
348
+
349
+ pulsar_consumer_acknowledge_cumulative_async(
350
+ this->cConsumer.get(), msg->GetCMessage().get(),
351
+ [](pulsar_result result, void *ctx) {
352
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
353
+ auto deferred = deferredContext->deferred;
354
+ delete deferredContext;
355
+
356
+ if (result != pulsar_result_Ok) {
357
+ deferred->Reject(std::string("Failed to acknowledge cumulatively: ") + pulsar_result_str(result));
358
+ } else {
359
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
360
+ }
361
+ },
362
+ ctx);
363
+
364
+ return deferred->Promise();
293
365
  }
294
366
 
295
- void Consumer::AcknowledgeCumulativeId(const Napi::CallbackInfo &info) {
296
- Napi::Object obj = info[0].As<Napi::Object>();
297
- MessageId *msgId = MessageId::Unwrap(obj);
298
- pulsar_consumer_acknowledge_cumulative_async_id(this->wrapper->cConsumer, msgId->GetCMessageId(), NULL,
299
- NULL);
367
+ Napi::Value Consumer::AcknowledgeCumulativeId(const Napi::CallbackInfo &info) {
368
+ auto obj = info[0].As<Napi::Object>();
369
+ auto *msgId = MessageId::Unwrap(obj);
370
+ auto deferred = ThreadSafeDeferred::New(Env());
371
+ auto ctx = new ExtDeferredContext(deferred);
372
+
373
+ pulsar_consumer_acknowledge_cumulative_async_id(
374
+ this->cConsumer.get(), msgId->GetCMessageId().get(),
375
+ [](pulsar_result result, void *ctx) {
376
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
377
+ auto deferred = deferredContext->deferred;
378
+ delete deferredContext;
379
+
380
+ if (result != pulsar_result_Ok) {
381
+ deferred->Reject(std::string("Failed to acknowledge cumulatively by id: ") +
382
+ pulsar_result_str(result));
383
+ } else {
384
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
385
+ }
386
+ },
387
+ ctx);
388
+
389
+ return deferred->Promise();
300
390
  }
301
391
 
302
- Napi::Value Consumer::IsConnected(const Napi::CallbackInfo &info) {
303
- Napi::Env env = info.Env();
304
- return Napi::Boolean::New(env, pulsar_consumer_is_connected(this->wrapper->cConsumer));
392
+ Napi::Value Consumer::Seek(const Napi::CallbackInfo &info) {
393
+ auto obj = info[0].As<Napi::Object>();
394
+ auto *msgId = MessageId::Unwrap(obj);
395
+ auto deferred = ThreadSafeDeferred::New(Env());
396
+ auto ctx = new ExtDeferredContext(deferred);
397
+
398
+ pulsar_consumer_seek_async(
399
+ this->cConsumer.get(), msgId->GetCMessageId().get(),
400
+ [](pulsar_result result, void *ctx) {
401
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
402
+ auto deferred = deferredContext->deferred;
403
+ delete deferredContext;
404
+
405
+ if (result != pulsar_result_Ok) {
406
+ deferred->Reject(std::string("Failed to seek message by id: ") + pulsar_result_str(result));
407
+ } else {
408
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
409
+ }
410
+ },
411
+ ctx);
412
+
413
+ return deferred->Promise();
305
414
  }
306
415
 
307
- class ConsumerCloseWorker : public Napi::AsyncWorker {
308
- public:
309
- ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer,
310
- Consumer *consumer)
311
- : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
312
- deferred(deferred),
313
- cConsumer(cConsumer),
314
- consumer(consumer) {}
315
-
316
- ~ConsumerCloseWorker() {}
317
- void Execute() {
318
- pulsar_consumer_pause_message_listener(this->cConsumer);
319
- pulsar_result result = pulsar_consumer_close(this->cConsumer);
320
- if (result != pulsar_result_Ok) {
321
- SetError(pulsar_result_str(result));
322
- }
323
- }
324
- void OnOK() {
325
- this->consumer->Cleanup();
326
- this->deferred.Resolve(Env().Null());
327
- }
328
- void OnError(const Napi::Error &e) {
329
- this->deferred.Reject(
330
- Napi::Error::New(Env(), std::string("Failed to close consumer: ") + e.Message()).Value());
331
- }
332
-
333
- private:
334
- Napi::Promise::Deferred deferred;
335
- pulsar_consumer_t *cConsumer;
336
- Consumer *consumer;
337
- };
338
-
339
- class ConsumerUnsubscribeWorker : public Napi::AsyncWorker {
340
- public:
341
- ConsumerUnsubscribeWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer,
342
- Consumer *consumer)
343
- : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
344
- deferred(deferred),
345
- cConsumer(cConsumer),
346
- consumer(consumer) {}
347
-
348
- ~ConsumerUnsubscribeWorker() {}
349
- void Execute() {
350
- pulsar_consumer_pause_message_listener(this->cConsumer);
351
- pulsar_result result = pulsar_consumer_unsubscribe(this->cConsumer);
352
- if (result != pulsar_result_Ok) {
353
- SetError(pulsar_result_str(result));
354
- }
355
- }
356
- void OnOK() {
357
- this->consumer->Cleanup();
358
- this->deferred.Resolve(Env().Null());
359
- }
360
- void OnError(const Napi::Error &e) {
361
- this->deferred.Reject(
362
- Napi::Error::New(Env(), std::string("Failed to unsubscribe consumer: ") + e.Message()).Value());
363
- }
416
+ Napi::Value Consumer::SeekTimestamp(const Napi::CallbackInfo &info) {
417
+ Napi::Number timestamp = info[0].As<Napi::Object>().ToNumber();
418
+ auto deferred = ThreadSafeDeferred::New(Env());
419
+ auto ctx = new ExtDeferredContext(deferred);
420
+
421
+ pulsar_consumer_seek_by_timestamp_async(
422
+ this->cConsumer.get(), timestamp.Int64Value(),
423
+ [](pulsar_result result, void *ctx) {
424
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
425
+ auto deferred = deferredContext->deferred;
426
+ delete deferredContext;
427
+
428
+ if (result != pulsar_result_Ok) {
429
+ deferred->Reject(std::string("Failed to seek message by timestamp: ") + pulsar_result_str(result));
430
+ } else {
431
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
432
+ }
433
+ },
434
+ ctx);
435
+
436
+ return deferred->Promise();
437
+ }
364
438
 
365
- private:
366
- Napi::Promise::Deferred deferred;
367
- pulsar_consumer_t *cConsumer;
368
- Consumer *consumer;
369
- };
439
+ Napi::Value Consumer::IsConnected(const Napi::CallbackInfo &info) {
440
+ Napi::Env env = info.Env();
441
+ return Napi::Boolean::New(env, pulsar_consumer_is_connected(this->cConsumer.get()));
442
+ }
370
443
 
371
444
  void Consumer::Cleanup() {
372
- if (this->listener) {
373
- this->CleanupListener();
445
+ if (this->listener != nullptr) {
446
+ pulsar_consumer_pause_message_listener(this->cConsumer.get());
447
+ this->listener->callback.Release();
448
+ this->listener = nullptr;
449
+ this->Unref();
374
450
  }
375
451
  }
376
452
 
377
- void Consumer::CleanupListener() {
378
- pulsar_consumer_pause_message_listener(this->wrapper->cConsumer);
379
- this->Unref();
380
- this->listener->callback.Release();
381
- this->listener = nullptr;
382
- }
383
-
384
453
  Napi::Value Consumer::Close(const Napi::CallbackInfo &info) {
385
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
386
- ConsumerCloseWorker *wk = new ConsumerCloseWorker(deferred, this->wrapper->cConsumer, this);
387
- wk->Queue();
388
- return deferred.Promise();
454
+ auto deferred = ThreadSafeDeferred::New(Env());
455
+ auto ctx = new ExtDeferredContext(deferred);
456
+ this->Cleanup();
457
+
458
+ pulsar_consumer_close_async(
459
+ this->cConsumer.get(),
460
+ [](pulsar_result result, void *ctx) {
461
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
462
+ auto deferred = deferredContext->deferred;
463
+ delete deferredContext;
464
+
465
+ if (result != pulsar_result_Ok) {
466
+ deferred->Reject(std::string("Failed to close consumer: ") + pulsar_result_str(result));
467
+ } else {
468
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
469
+ }
470
+ },
471
+ ctx);
472
+
473
+ return deferred->Promise();
389
474
  }
390
475
 
391
476
  Napi::Value Consumer::Unsubscribe(const Napi::CallbackInfo &info) {
392
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
393
- ConsumerUnsubscribeWorker *wk = new ConsumerUnsubscribeWorker(deferred, this->wrapper->cConsumer, this);
394
- wk->Queue();
395
- return deferred.Promise();
477
+ auto deferred = ThreadSafeDeferred::New(Env());
478
+ auto ctx = new ExtDeferredContext(deferred);
479
+
480
+ pulsar_consumer_pause_message_listener(this->cConsumer.get());
481
+ pulsar_consumer_unsubscribe_async(
482
+ this->cConsumer.get(),
483
+ [](pulsar_result result, void *ctx) {
484
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
485
+ auto deferred = deferredContext->deferred;
486
+ delete deferredContext;
487
+
488
+ if (result != pulsar_result_Ok) {
489
+ deferred->Reject(std::string("Failed to unsubscribe consumer: ") + pulsar_result_str(result));
490
+ } else {
491
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
492
+ }
493
+ },
494
+ ctx);
495
+
496
+ return deferred->Promise();
396
497
  }
397
498
 
398
- Consumer::~Consumer() {
399
- if (this->listener) {
400
- this->CleanupListener();
401
- }
402
- }
499
+ Consumer::~Consumer() { this->Cleanup(); }