pulsar-client 1.6.2 → 1.8.0-rc1

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 +65 -0
  3. package/.github/workflows/ci-build-release-napi.yml +195 -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 +242 -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 +14 -12
  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 +8 -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/Reader.cc CHANGED
@@ -20,6 +20,8 @@
20
20
  #include "Message.h"
21
21
  #include "Reader.h"
22
22
  #include "ReaderConfig.h"
23
+ #include "MessageId.h"
24
+ #include "ThreadSafeDeferred.h"
23
25
  #include <pulsar/c/result.h>
24
26
  #include <pulsar/c/reader.h>
25
27
  #include <atomic>
@@ -35,6 +37,8 @@ void Reader::Init(Napi::Env env, Napi::Object exports) {
35
37
  InstanceMethod("readNext", &Reader::ReadNext),
36
38
  InstanceMethod("hasNext", &Reader::HasNext),
37
39
  InstanceMethod("isConnected", &Reader::IsConnected),
40
+ InstanceMethod("seek", &Reader::Seek),
41
+ InstanceMethod("seekTimestamp", &Reader::SeekTimestamp),
38
42
  InstanceMethod("close", &Reader::Close),
39
43
  });
40
44
 
@@ -43,10 +47,11 @@ void Reader::Init(Napi::Env env, Napi::Object exports) {
43
47
  }
44
48
 
45
49
  struct ReaderListenerProxyData {
46
- pulsar_message_t *cMessage;
50
+ std::shared_ptr<pulsar_message_t> cMessage;
47
51
  Reader *reader;
48
52
 
49
- ReaderListenerProxyData(pulsar_message_t *cMessage, Reader *reader) : cMessage(cMessage), reader(reader) {}
53
+ ReaderListenerProxyData(std::shared_ptr<pulsar_message_t> cMessage, Reader *reader)
54
+ : cMessage(cMessage), reader(reader) {}
50
55
  };
51
56
 
52
57
  void ReaderListenerProxy(Napi::Env env, Napi::Function jsCallback, ReaderListenerProxyData *data) {
@@ -57,7 +62,8 @@ void ReaderListenerProxy(Napi::Env env, Napi::Function jsCallback, ReaderListene
57
62
  jsCallback.Call({msg, reader->Value()});
58
63
  }
59
64
 
60
- void ReaderListener(pulsar_reader_t *cReader, pulsar_message_t *cMessage, void *ctx) {
65
+ void ReaderListener(pulsar_reader_t *rawReader, pulsar_message_t *rawMessage, void *ctx) {
66
+ std::shared_ptr<pulsar_message_t> cMessage(rawMessage, pulsar_message_free);
61
67
  ReaderListenerCallback *readerListenerCallback = (ReaderListenerCallback *)ctx;
62
68
  Reader *reader = (Reader *)readerListenerCallback->reader;
63
69
  if (readerListenerCallback->callback.Acquire() != napi_ok) {
@@ -68,101 +74,85 @@ void ReaderListener(pulsar_reader_t *cReader, pulsar_message_t *cMessage, void *
68
74
  readerListenerCallback->callback.Release();
69
75
  }
70
76
 
71
- void Reader::SetCReader(std::shared_ptr<CReaderWrapper> cReader) { this->wrapper = cReader; }
77
+ void Reader::SetCReader(std::shared_ptr<pulsar_reader_t> cReader) { this->cReader = cReader; }
72
78
  void Reader::SetListenerCallback(ReaderListenerCallback *listener) {
73
- if (listener) {
74
- // Maintain reference to reader, so it won't get garbage collected
75
- // since, when we have a listener, we don't have to maintain reference to reader (in js code)
76
- this->Ref();
79
+ if (this->listener != nullptr) {
80
+ // It is only safe to set the listener once for the lifecycle of the Reader
81
+ return;
82
+ }
77
83
 
78
- // Pass reader as argument
84
+ if (listener != nullptr) {
79
85
  listener->reader = this;
86
+ // If a reader listener is set, the Reader instance is kept alive even if it goes out of scope in JS code.
87
+ this->Ref();
88
+ this->listener = listener;
80
89
  }
81
-
82
- this->listener = listener;
83
90
  }
84
91
 
85
92
  Reader::Reader(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Reader>(info), listener(nullptr) {}
86
93
 
87
- class ReaderNewInstanceWorker : public Napi::AsyncWorker {
88
- public:
89
- ReaderNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
90
- ReaderConfig *readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper)
91
- : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
92
- deferred(deferred),
93
- cClient(cClient),
94
- readerConfig(readerConfig),
95
- readerWrapper(readerWrapper),
96
- done(false) {}
97
- ~ReaderNewInstanceWorker() {}
98
- void Execute() {
99
- const std::string &topic = this->readerConfig->GetTopic();
100
- if (topic.empty()) {
101
- std::string msg("Topic is required and must be specified as a string when creating reader");
102
- SetError(msg);
103
- return;
104
- }
105
- if (this->readerConfig->GetCStartMessageId() == nullptr) {
106
- std::string msg(
107
- "StartMessageId is required and must be specified as a MessageId object when creating reader");
108
- SetError(msg);
109
- return;
110
- }
111
-
112
- pulsar_client_create_reader_async(this->cClient, topic.c_str(), this->readerConfig->GetCStartMessageId(),
113
- this->readerConfig->GetCReaderConfig(),
114
- &ReaderNewInstanceWorker::createReaderCallback, (void *)this);
115
-
116
- while (!done) {
117
- std::this_thread::yield();
118
- }
119
- }
120
- void OnOK() {
121
- Napi::Object obj = Reader::constructor.New({});
122
- Reader *reader = Reader::Unwrap(obj);
123
- reader->SetCReader(this->readerWrapper);
124
- reader->SetListenerCallback(this->listener);
125
- this->deferred.Resolve(obj);
126
- }
127
- void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
94
+ struct ReaderNewInstanceContext {
95
+ ReaderNewInstanceContext(std::shared_ptr<ThreadSafeDeferred> deferred,
96
+ std::shared_ptr<pulsar_client_t> cClient,
97
+ std::shared_ptr<ReaderConfig> readerConfig)
98
+ : deferred(deferred), cClient(cClient), readerConfig(readerConfig){};
99
+ std::shared_ptr<ThreadSafeDeferred> deferred;
100
+ std::shared_ptr<pulsar_client_t> cClient;
101
+ std::shared_ptr<ReaderConfig> readerConfig;
102
+
103
+ static void createReaderCallback(pulsar_result result, pulsar_reader_t *rawReader, void *ctx) {
104
+ auto instanceContext = static_cast<ReaderNewInstanceContext *>(ctx);
105
+ auto deferred = instanceContext->deferred;
106
+ auto cClient = instanceContext->cClient;
107
+ auto readerConfig = instanceContext->readerConfig;
108
+ delete instanceContext;
128
109
 
129
- private:
130
- Napi::Promise::Deferred deferred;
131
- pulsar_client_t *cClient;
132
- pulsar_reader_t *cReader;
133
- ReaderConfig *readerConfig;
134
- ReaderListenerCallback *listener;
135
- std::shared_ptr<CReaderWrapper> readerWrapper;
136
- std::atomic<bool> done;
137
- static void createReaderCallback(pulsar_result result, pulsar_reader_t *reader, void *ctx) {
138
- ReaderNewInstanceWorker *worker = (ReaderNewInstanceWorker *)ctx;
139
110
  if (result != pulsar_result_Ok) {
140
- worker->SetError(std::string("Failed to create reader: ") + pulsar_result_str(result));
141
- } else {
142
- worker->readerWrapper->cReader = reader;
143
- worker->listener = worker->readerConfig->GetListenerCallback();
111
+ return deferred->Reject(std::string("Failed to create reader: ") + pulsar_result_str(result));
144
112
  }
145
113
 
146
- delete worker->readerConfig;
147
- worker->done = true;
114
+ auto cReader = std::shared_ptr<pulsar_reader_t>(rawReader, pulsar_reader_free);
115
+
116
+ deferred->Resolve([cReader, readerConfig](const Napi::Env env) {
117
+ Napi::Object obj = Reader::constructor.New({});
118
+ Reader *reader = Reader::Unwrap(obj);
119
+ reader->SetCReader(cReader);
120
+ reader->SetListenerCallback(readerConfig->GetListenerCallback());
121
+ return obj;
122
+ });
148
123
  }
149
124
  };
150
125
 
151
- Napi::Value Reader::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
152
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
126
+ Napi::Value Reader::NewInstance(const Napi::CallbackInfo &info, std::shared_ptr<pulsar_client_t> cClient) {
127
+ auto deferred = ThreadSafeDeferred::New(info.Env());
153
128
  Napi::Object config = info[0].As<Napi::Object>();
154
129
 
155
- std::shared_ptr<CReaderWrapper> readerWrapper = std::make_shared<CReaderWrapper>();
130
+ auto readerConfig = std::make_shared<ReaderConfig>(config, &ReaderListener);
156
131
 
157
- ReaderConfig *readerConfig = new ReaderConfig(config, readerWrapper, &ReaderListener);
158
- ReaderNewInstanceWorker *wk = new ReaderNewInstanceWorker(deferred, cClient, readerConfig, readerWrapper);
159
- wk->Queue();
160
- return deferred.Promise();
132
+ const std::string &topic = readerConfig->GetTopic();
133
+ if (topic.empty()) {
134
+ deferred->Reject(std::string("Topic is required and must be specified as a string when creating reader"));
135
+ return deferred->Promise();
136
+ }
137
+ if (readerConfig->GetCStartMessageId().get() == nullptr) {
138
+ deferred->Reject(std::string(
139
+ "StartMessageId is required and must be specified as a MessageId object when creating reader"));
140
+ return deferred->Promise();
141
+ }
142
+
143
+ auto ctx = new ReaderNewInstanceContext(deferred, cClient, readerConfig);
144
+
145
+ pulsar_client_create_reader_async(cClient.get(), topic.c_str(), readerConfig->GetCStartMessageId().get(),
146
+ readerConfig->GetCReaderConfig().get(),
147
+ &ReaderNewInstanceContext::createReaderCallback, ctx);
148
+
149
+ return deferred->Promise();
161
150
  }
162
151
 
152
+ // We still need a read worker because the c api is missing the equivalent async definition
163
153
  class ReaderReadNextWorker : public Napi::AsyncWorker {
164
154
  public:
165
- ReaderReadNextWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader,
155
+ ReaderReadNextWorker(const Napi::Promise::Deferred &deferred, std::shared_ptr<pulsar_reader_t> cReader,
166
156
  int64_t timeout = -1)
167
157
  : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
168
158
  deferred(deferred),
@@ -171,14 +161,16 @@ class ReaderReadNextWorker : public Napi::AsyncWorker {
171
161
  ~ReaderReadNextWorker() {}
172
162
  void Execute() {
173
163
  pulsar_result result;
164
+ pulsar_message_t *rawMessage;
174
165
  if (timeout > 0) {
175
- result = pulsar_reader_read_next_with_timeout(this->cReader, &(this->cMessage), timeout);
166
+ result = pulsar_reader_read_next_with_timeout(this->cReader.get(), &rawMessage, timeout);
176
167
  } else {
177
- result = pulsar_reader_read_next(this->cReader, &(this->cMessage));
168
+ result = pulsar_reader_read_next(this->cReader.get(), &rawMessage);
178
169
  }
179
170
  if (result != pulsar_result_Ok) {
180
- SetError(std::string("Failed to received message ") + pulsar_result_str(result));
171
+ SetError(std::string("Failed to receive message: ") + pulsar_result_str(result));
181
172
  }
173
+ this->cMessage = std::shared_ptr<pulsar_message_t>(rawMessage, pulsar_message_free);
182
174
  }
183
175
  void OnOK() {
184
176
  Napi::Object obj = Message::NewInstance({}, this->cMessage);
@@ -188,20 +180,19 @@ class ReaderReadNextWorker : public Napi::AsyncWorker {
188
180
 
189
181
  private:
190
182
  Napi::Promise::Deferred deferred;
191
- pulsar_reader_t *cReader;
192
- pulsar_message_t *cMessage;
183
+ std::shared_ptr<pulsar_reader_t> cReader;
184
+ std::shared_ptr<pulsar_message_t> cMessage;
193
185
  int64_t timeout;
194
186
  };
195
187
 
196
188
  Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
197
189
  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
198
190
  if (info[0].IsUndefined()) {
199
- ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->wrapper->cReader);
191
+ ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader);
200
192
  wk->Queue();
201
193
  } else {
202
194
  Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
203
- ReaderReadNextWorker *wk =
204
- new ReaderReadNextWorker(deferred, this->wrapper->cReader, timeout.Int64Value());
195
+ ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader, timeout.Int64Value());
205
196
  wk->Queue();
206
197
  }
207
198
  return deferred.Promise();
@@ -209,7 +200,7 @@ Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
209
200
 
210
201
  Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
211
202
  int value = 0;
212
- pulsar_result result = pulsar_reader_has_message_available(this->wrapper->cReader, &value);
203
+ pulsar_result result = pulsar_reader_has_message_available(this->cReader.get(), &value);
213
204
  if (result != pulsar_result_Ok) {
214
205
  Napi::Error::New(info.Env(), "Failed to check if next message is available").ThrowAsJavaScriptException();
215
206
  return Napi::Boolean::New(info.Env(), false);
@@ -222,57 +213,85 @@ Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
222
213
 
223
214
  Napi::Value Reader::IsConnected(const Napi::CallbackInfo &info) {
224
215
  Napi::Env env = info.Env();
225
- return Napi::Boolean::New(env, pulsar_reader_is_connected(this->wrapper->cReader));
216
+ return Napi::Boolean::New(env, pulsar_reader_is_connected(this->cReader.get()));
226
217
  }
227
218
 
228
- class ReaderCloseWorker : public Napi::AsyncWorker {
229
- public:
230
- ReaderCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader, Reader *reader)
231
- : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
232
- deferred(deferred),
233
- cReader(cReader),
234
- reader(reader) {}
235
- ~ReaderCloseWorker() {}
236
- void Execute() {
237
- pulsar_result result = pulsar_reader_close(this->cReader);
238
- if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
239
- }
240
- void OnOK() {
241
- this->reader->Cleanup();
242
- this->deferred.Resolve(Env().Null());
243
- }
244
- void OnError(const Napi::Error &e) {
245
- this->deferred.Reject(
246
- Napi::Error::New(Env(), std::string("Failed to close reader: ") + e.Message()).Value());
247
- }
219
+ Napi::Value Reader::Seek(const Napi::CallbackInfo &info) {
220
+ auto obj = info[0].As<Napi::Object>();
221
+ auto *msgId = MessageId::Unwrap(obj);
222
+ auto deferred = ThreadSafeDeferred::New(Env());
223
+ auto ctx = new ExtDeferredContext(deferred);
224
+
225
+ pulsar_reader_seek_async(
226
+ this->cReader.get(), msgId->GetCMessageId().get(),
227
+ [](pulsar_result result, void *ctx) {
228
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
229
+ auto deferred = deferredContext->deferred;
230
+ delete deferredContext;
231
+
232
+ if (result != pulsar_result_Ok) {
233
+ deferred->Reject(std::string("Failed to seek message by id: ") + pulsar_result_str(result));
234
+ } else {
235
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
236
+ }
237
+ },
238
+ ctx);
239
+
240
+ return deferred->Promise();
241
+ }
248
242
 
249
- private:
250
- Napi::Promise::Deferred deferred;
251
- pulsar_reader_t *cReader;
252
- Reader *reader;
253
- };
243
+ Napi::Value Reader::SeekTimestamp(const Napi::CallbackInfo &info) {
244
+ Napi::Number timestamp = info[0].As<Napi::Object>().ToNumber();
245
+ auto deferred = ThreadSafeDeferred::New(Env());
246
+ auto ctx = new ExtDeferredContext(deferred);
247
+
248
+ pulsar_reader_seek_by_timestamp_async(
249
+ this->cReader.get(), timestamp.Int64Value(),
250
+ [](pulsar_result result, void *ctx) {
251
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
252
+ auto deferred = deferredContext->deferred;
253
+ delete deferredContext;
254
+
255
+ if (result != pulsar_result_Ok) {
256
+ deferred->Reject(std::string("Failed to seek message by timestamp: ") + pulsar_result_str(result));
257
+ } else {
258
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
259
+ }
260
+ },
261
+ ctx);
262
+
263
+ return deferred->Promise();
264
+ }
254
265
 
255
266
  Napi::Value Reader::Close(const Napi::CallbackInfo &info) {
256
- Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
257
- ReaderCloseWorker *wk = new ReaderCloseWorker(deferred, this->wrapper->cReader, this);
258
- wk->Queue();
259
- return deferred.Promise();
267
+ auto deferred = ThreadSafeDeferred::New(Env());
268
+ auto ctx = new ExtDeferredContext(deferred);
269
+ this->Cleanup();
270
+
271
+ pulsar_reader_close_async(
272
+ this->cReader.get(),
273
+ [](pulsar_result result, void *ctx) {
274
+ auto deferredContext = static_cast<ExtDeferredContext *>(ctx);
275
+ auto deferred = deferredContext->deferred;
276
+ delete deferredContext;
277
+
278
+ if (result != pulsar_result_Ok) {
279
+ deferred->Reject(std::string("Failed to close reader: ") + pulsar_result_str(result));
280
+ } else {
281
+ deferred->Resolve(THREADSAFE_DEFERRED_RESOLVER(env.Null()));
282
+ }
283
+ },
284
+ ctx);
285
+
286
+ return deferred->Promise();
260
287
  }
261
288
 
262
289
  void Reader::Cleanup() {
263
- if (this->listener) {
264
- this->CleanupListener();
290
+ if (this->listener != nullptr) {
291
+ this->listener->callback.Release();
292
+ this->Unref();
293
+ this->listener = nullptr;
265
294
  }
266
295
  }
267
296
 
268
- void Reader::CleanupListener() {
269
- this->Unref();
270
- this->listener->callback.Release();
271
- this->listener = nullptr;
272
- }
273
-
274
- Reader::~Reader() {
275
- if (this->listener) {
276
- this->CleanupListener();
277
- }
278
- }
297
+ Reader::~Reader() { this->Cleanup(); }
package/src/Reader.h CHANGED
@@ -27,21 +27,23 @@
27
27
  class Reader : public Napi::ObjectWrap<Reader> {
28
28
  public:
29
29
  static void Init(Napi::Env env, Napi::Object exports);
30
- static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
30
+ static Napi::Value NewInstance(const Napi::CallbackInfo &info, std::shared_ptr<pulsar_client_t> cClient);
31
31
  static Napi::FunctionReference constructor;
32
32
  Reader(const Napi::CallbackInfo &info);
33
33
  ~Reader();
34
- void SetCReader(std::shared_ptr<CReaderWrapper> cReader);
34
+ void SetCReader(std::shared_ptr<pulsar_reader_t> cReader);
35
35
  void SetListenerCallback(ReaderListenerCallback *listener);
36
36
  void Cleanup();
37
37
 
38
38
  private:
39
- std::shared_ptr<CReaderWrapper> wrapper;
39
+ std::shared_ptr<pulsar_reader_t> cReader;
40
40
  ReaderListenerCallback *listener;
41
41
 
42
42
  Napi::Value ReadNext(const Napi::CallbackInfo &info);
43
43
  Napi::Value HasNext(const Napi::CallbackInfo &info);
44
44
  Napi::Value IsConnected(const Napi::CallbackInfo &info);
45
+ Napi::Value Seek(const Napi::CallbackInfo &info);
46
+ Napi::Value SeekTimestamp(const Napi::CallbackInfo &info);
45
47
  Napi::Value Close(const Napi::CallbackInfo &info);
46
48
  void CleanupListener();
47
49
  };
@@ -31,10 +31,10 @@ static const std::string CFG_LISTENER = "listener";
31
31
 
32
32
  void FinalizeListenerCallback(Napi::Env env, ReaderListenerCallback *cb, void *) { delete cb; }
33
33
 
34
- ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper,
35
- pulsar_reader_listener readerListener)
34
+ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, pulsar_reader_listener readerListener)
36
35
  : topic(""), cStartMessageId(NULL), listener(nullptr) {
37
- this->cReaderConfig = pulsar_reader_configuration_create();
36
+ this->cReaderConfig = std::shared_ptr<pulsar_reader_configuration_t>(pulsar_reader_configuration_create(),
37
+ pulsar_reader_configuration_free);
38
38
 
39
39
  if (readerConfig.Has(CFG_TOPIC) && readerConfig.Get(CFG_TOPIC).IsString()) {
40
40
  this->topic = readerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
@@ -48,14 +48,14 @@ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CRe
48
48
  if (readerConfig.Has(CFG_RECV_QUEUE) && readerConfig.Get(CFG_RECV_QUEUE).IsNumber()) {
49
49
  int32_t receiverQueueSize = readerConfig.Get(CFG_RECV_QUEUE).ToNumber().Int32Value();
50
50
  if (receiverQueueSize >= 0) {
51
- pulsar_reader_configuration_set_receiver_queue_size(this->cReaderConfig, receiverQueueSize);
51
+ pulsar_reader_configuration_set_receiver_queue_size(this->cReaderConfig.get(), receiverQueueSize);
52
52
  }
53
53
  }
54
54
 
55
55
  if (readerConfig.Has(CFG_READER_NAME) && readerConfig.Get(CFG_READER_NAME).IsString()) {
56
56
  std::string readerName = readerConfig.Get(CFG_READER_NAME).ToString().Utf8Value();
57
57
  if (!readerName.empty())
58
- pulsar_reader_configuration_set_reader_name(this->cReaderConfig, readerName.c_str());
58
+ pulsar_reader_configuration_set_reader_name(this->cReaderConfig.get(), readerName.c_str());
59
59
  }
60
60
 
61
61
  if (readerConfig.Has(CFG_SUBSCRIPTION_ROLE_PREFIX) &&
@@ -63,13 +63,13 @@ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CRe
63
63
  std::string subscriptionRolePrefix =
64
64
  readerConfig.Get(CFG_SUBSCRIPTION_ROLE_PREFIX).ToString().Utf8Value();
65
65
  if (!subscriptionRolePrefix.empty())
66
- pulsar_reader_configuration_set_reader_name(this->cReaderConfig, subscriptionRolePrefix.c_str());
66
+ pulsar_reader_configuration_set_reader_name(this->cReaderConfig.get(), subscriptionRolePrefix.c_str());
67
67
  }
68
68
 
69
69
  if (readerConfig.Has(CFG_READ_COMPACTED) && readerConfig.Get(CFG_READ_COMPACTED).IsBoolean()) {
70
70
  bool readCompacted = readerConfig.Get(CFG_READ_COMPACTED).ToBoolean();
71
71
  if (readCompacted) {
72
- pulsar_reader_configuration_set_read_compacted(this->cReaderConfig, 1);
72
+ pulsar_reader_configuration_set_read_compacted(this->cReaderConfig.get(), 1);
73
73
  }
74
74
  }
75
75
 
@@ -79,33 +79,27 @@ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CRe
79
79
  readerConfig.Env(), readerConfig.Get(CFG_LISTENER).As<Napi::Function>(), "Reader Listener Callback",
80
80
  1, 1, (void *)NULL, FinalizeListenerCallback, listener);
81
81
  this->listener->callback = std::move(callback);
82
- pulsar_reader_configuration_set_reader_listener(this->cReaderConfig, readerListener, this->listener);
82
+ pulsar_reader_configuration_set_reader_listener(this->cReaderConfig.get(), readerListener,
83
+ this->listener);
83
84
  }
84
85
  }
85
86
 
86
87
  ReaderConfig::~ReaderConfig() {
87
- pulsar_reader_configuration_free(this->cReaderConfig);
88
- if (this->listener) {
88
+ if (this->listener != nullptr) {
89
89
  this->listener->callback.Release();
90
90
  }
91
91
  }
92
92
 
93
- pulsar_reader_configuration_t *ReaderConfig::GetCReaderConfig() { return this->cReaderConfig; }
93
+ std::shared_ptr<pulsar_reader_configuration_t> ReaderConfig::GetCReaderConfig() {
94
+ return this->cReaderConfig;
95
+ }
94
96
 
95
97
  std::string ReaderConfig::GetTopic() { return this->topic; }
96
98
 
97
- pulsar_message_id_t *ReaderConfig::GetCStartMessageId() { return this->cStartMessageId; }
99
+ std::shared_ptr<pulsar_message_id_t> ReaderConfig::GetCStartMessageId() { return this->cStartMessageId; }
98
100
 
99
101
  ReaderListenerCallback *ReaderConfig::GetListenerCallback() {
100
102
  ReaderListenerCallback *cb = this->listener;
101
103
  this->listener = nullptr;
102
104
  return cb;
103
105
  }
104
-
105
- CReaderWrapper::CReaderWrapper() : cReader(nullptr) {}
106
-
107
- CReaderWrapper::~CReaderWrapper() {
108
- if (this->cReader) {
109
- pulsar_reader_free(this->cReader);
110
- }
111
- }
@@ -28,18 +28,17 @@
28
28
 
29
29
  class ReaderConfig {
30
30
  public:
31
- ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper,
32
- pulsar_reader_listener readerListener);
31
+ ReaderConfig(const Napi::Object &readerConfig, pulsar_reader_listener readerListener);
33
32
  ~ReaderConfig();
34
- pulsar_reader_configuration_t *GetCReaderConfig();
35
- pulsar_message_id_t *GetCStartMessageId();
33
+ std::shared_ptr<pulsar_reader_configuration_t> GetCReaderConfig();
34
+ std::shared_ptr<pulsar_message_id_t> GetCStartMessageId();
36
35
  std::string GetTopic();
37
36
  ReaderListenerCallback *GetListenerCallback();
38
37
 
39
38
  private:
40
39
  std::string topic;
41
- pulsar_message_id_t *cStartMessageId;
42
- pulsar_reader_configuration_t *cReaderConfig;
40
+ std::shared_ptr<pulsar_message_id_t> cStartMessageId;
41
+ std::shared_ptr<pulsar_reader_configuration_t> cReaderConfig;
43
42
  ReaderListenerCallback *listener;
44
43
  };
45
44
 
@@ -21,19 +21,14 @@
21
21
  #define READER_LISTENER_H
22
22
 
23
23
  #include <napi.h>
24
- #include <pulsar/c/client.h>
25
-
26
- struct CReaderWrapper {
27
- pulsar_reader_t *cReader;
28
- CReaderWrapper();
29
- ~CReaderWrapper();
30
- };
31
24
 
32
25
  struct ReaderListenerCallback {
33
26
  Napi::ThreadSafeFunction callback;
34
27
 
35
28
  // Using reader as void* since the ReaderListenerCallback is shared between Config and Reader.
36
29
  void *reader;
30
+
31
+ ReaderListenerCallback() : reader(nullptr) {}
37
32
  };
38
33
 
39
34
  #endif
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ #include "SchemaInfo.h"
20
+ #include <map>
21
+
22
+ static const std::string CFG_SCHEMA_TYPE = "schemaType";
23
+ static const std::string CFG_NAME = "name";
24
+ static const std::string CFG_SCHEMA = "schema";
25
+ static const std::string CFG_PROPS = "properties";
26
+
27
+ static const std::map<std::string, pulsar_schema_type> SCHEMA_TYPE = {{"None", pulsar_None},
28
+ {"String", pulsar_String},
29
+ {"Json", pulsar_Json},
30
+ {"Protobuf", pulsar_Protobuf},
31
+ {"Avro", pulsar_Avro},
32
+ {"Boolean", pulsar_Boolean},
33
+ {"Int8", pulsar_Int8},
34
+ {"Int16", pulsar_Int16},
35
+ {"Int32", pulsar_Int32},
36
+ {"Int64", pulsar_Int64},
37
+ {"Float32", pulsar_Float32},
38
+ {"Float64", pulsar_Float64},
39
+ {"KeyValue", pulsar_KeyValue},
40
+ {"Bytes", pulsar_Bytes},
41
+ {"AutoConsume", pulsar_AutoConsume},
42
+ {"AutoPublish", pulsar_AutoPublish}};
43
+
44
+ SchemaInfo::SchemaInfo(const Napi::Object &schemaInfo) : cSchemaType(pulsar_Bytes), name("BYTES"), schema() {
45
+ this->cProperties = pulsar_string_map_create();
46
+ if (schemaInfo.Has(CFG_SCHEMA_TYPE) && schemaInfo.Get(CFG_SCHEMA_TYPE).IsString()) {
47
+ this->name = schemaInfo.Get(CFG_SCHEMA_TYPE).ToString().Utf8Value();
48
+ this->cSchemaType = SCHEMA_TYPE.at(schemaInfo.Get(CFG_SCHEMA_TYPE).ToString().Utf8Value());
49
+ }
50
+ if (schemaInfo.Has(CFG_NAME) && schemaInfo.Get(CFG_NAME).IsString()) {
51
+ this->name = schemaInfo.Get(CFG_NAME).ToString().Utf8Value();
52
+ }
53
+ if (schemaInfo.Has(CFG_SCHEMA) && schemaInfo.Get(CFG_SCHEMA).IsString()) {
54
+ this->schema = schemaInfo.Get(CFG_SCHEMA).ToString().Utf8Value();
55
+ }
56
+ if (schemaInfo.Has(CFG_PROPS) && schemaInfo.Get(CFG_PROPS).IsObject()) {
57
+ Napi::Object propObj = schemaInfo.Get(CFG_PROPS).ToObject();
58
+ Napi::Array arr = propObj.GetPropertyNames();
59
+ int size = arr.Length();
60
+ for (int i = 0; i < size; i++) {
61
+ Napi::String key = arr.Get(i).ToString();
62
+ Napi::String value = propObj.Get(key).ToString();
63
+ pulsar_string_map_put(this->cProperties, key.Utf8Value().c_str(), value.Utf8Value().c_str());
64
+ }
65
+ }
66
+ }
67
+
68
+ void SchemaInfo::SetProducerSchema(std::shared_ptr<pulsar_producer_configuration_t> cProducerConfiguration) {
69
+ pulsar_producer_configuration_set_schema_info(cProducerConfiguration.get(), this->cSchemaType,
70
+ this->name.c_str(), this->schema.c_str(), this->cProperties);
71
+ }
72
+
73
+ void SchemaInfo::SetConsumerSchema(std::shared_ptr<pulsar_consumer_configuration_t> cConsumerConfiguration) {
74
+ pulsar_consumer_configuration_set_schema_info(cConsumerConfiguration.get(), this->cSchemaType,
75
+ this->name.c_str(), this->schema.c_str(), this->cProperties);
76
+ }
77
+
78
+ SchemaInfo::~SchemaInfo() { pulsar_string_map_free(this->cProperties); }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ #ifndef SCHEMA_INFO_H
21
+ #define SCHEMA_INFO_H
22
+
23
+ #include <napi.h>
24
+ #include <pulsar/c/producer_configuration.h>
25
+ #include <pulsar/c/consumer_configuration.h>
26
+
27
+ class SchemaInfo {
28
+ public:
29
+ SchemaInfo(const Napi::Object &schemaInfo);
30
+ ~SchemaInfo();
31
+ void SetProducerSchema(std::shared_ptr<pulsar_producer_configuration_t> cProducerConfiguration);
32
+ void SetConsumerSchema(std::shared_ptr<pulsar_consumer_configuration_t> cConsumerConfiguration);
33
+
34
+ private:
35
+ pulsar_schema_type cSchemaType;
36
+ std::string name;
37
+ std::string schema;
38
+ pulsar_string_map_t *cProperties;
39
+ };
40
+
41
+ #endif