pulsar-client 1.3.1 → 1.4.1-rc.1

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.
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEAtKWwgqdnTYrOCv+j1MkTWfSH0wCsHZZca9wAW3qP4uuhlBvn
3
+ b10JcFf5ZjzP9BSXK+tHmI8uoN368vEv6yhURHM4yuXqzCxzuAwkQSo39rzX8PGC
4
+ 7qdjCN7LDJ3MnqiBIrUsSaEP1wrNsB1kI+o9ER1e5O/uEPAotP933hHQ0J2hMEek
5
+ HqL7sBlJ98h6NmsicEaUkardk0TOXrlkjC+cMd8ZbGScPqI9M38bmn3OLxFTn1vt
6
+ hpvnXLvCmG4M+6xtYtD+npcVPZw1i1R90fMs7ppZnRbv8Hc/DFdOKVQIgam6CDdn
7
+ NKgW7c7IBMrP0AEm37HTu0LSOjP2OHXlvvlQGQIDAQABAoIBAAaJFAi2C7u3cNrf
8
+ AstY9vVDLoLIvHFZlkBktjKZDYmVIsRb+hSCViwVUrWLL67R6+Iv4eg4DeTOAx00
9
+ 8pncXKgZTw2wIb1/QjR/Y/RjlaC8lkdmRWli7udMQCZVsyhuSjW6Pj7vr8YE4woj
10
+ FhNijxEGcf9wWrmMJrzdnTWQiXByo+eTvUQ9BPgPGrRjsMZmTkLyAVJff2DfxO5b
11
+ IWFDYDJcyYAMCIMQu7vys/I50ou6ilb1CO6QM6Z7KpPeOoVFPwtzbh8cf9xM8UNS
12
+ j6J/JmdWhgI34GS3NA68xTQ6PV7zjnhCc+iccm3JKyzGXwaApAZ+Eoce/9j4WKmu
13
+ 5B4ziR0CgYEA3l/9OHbl1zmyV+rRxWOIj/i2rTvHzwBnbnPJyuemL5VMFdpGodQ3
14
+ vwHvyQmcECRVRxmXojQ4QuPPHs3qp6wEEFPCWxChLSTxlUc85SOFHWU2O99jV7zI
15
+ 7+JOpDK/Mstsx9nHgXduJF+glTFtA3LH8Oqylzu2aFPsprwKuZf94Q8CgYEAz/Zx
16
+ akEG+PEMtP5YS28cX5XfjsIX/V26Fs6/sH16QjUIEddE5T4fCuokxCjSiwUcWhml
17
+ pHEJ5S5xp3VYRfISW3jRW3qstIH1tpZipB6+S0zTuJmLJbA3IiWEg2rtMt7X1uJv
18
+ A/bYOqe0hOPTuXuZdtVZ0nMTKk7GG8O6VkBI7FcCgYEAkDfCmscJgs7JahlBWHmX
19
+ zH9pwem+SPKjIc/4NB6N+dgikx2Pp05hpP/VihUwYIufvs/LNogVYNQrtHepUnrN
20
+ 2+TmbHbZgNSv1Ldxt82UfB7y0FutKu6lhmXHyNecho3Fi8sih0V0aiSWmYuHfrAH
21
+ GaiskEZKo1iiZvQXJIx9O2MCgYATBf0r9hTYMtyxtc6H3/sdd01C9thQ8gDy0yjP
22
+ 0Tqc0dMSJroDqmIWkoKYew9/bhFA4LW5TCnWkCAPbHmNtG4fdfbYwmkH/hdnA2y0
23
+ jKdlpfp8GXeUFAGHGx17FA3sqFvgKUh0eWEgRHUL7vdQMVFBgJS93o7zQM94fLgP
24
+ 6cOB8wKBgFcGV4GjI2Ww9cillaC554MvoSjf8B/+04kXzDOh8iYIIzO9EUil1jjK
25
+ Jvxp4hnLzTKWbux3MEWqurLkYas6GpKBjw+iNOCar6YdqWGVqM3RUx7PTUaZwkKx
26
+ UdP63IfY7iZCIT/QbyHQvIUe2MaiVnH+ulxdkK6Y5e7gxcbckIH4
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtKWwgqdnTYrOCv+j1MkT
3
+ WfSH0wCsHZZca9wAW3qP4uuhlBvnb10JcFf5ZjzP9BSXK+tHmI8uoN368vEv6yhU
4
+ RHM4yuXqzCxzuAwkQSo39rzX8PGC7qdjCN7LDJ3MnqiBIrUsSaEP1wrNsB1kI+o9
5
+ ER1e5O/uEPAotP933hHQ0J2hMEekHqL7sBlJ98h6NmsicEaUkardk0TOXrlkjC+c
6
+ Md8ZbGScPqI9M38bmn3OLxFTn1vthpvnXLvCmG4M+6xtYtD+npcVPZw1i1R90fMs
7
+ 7ppZnRbv8Hc/DFdOKVQIgam6CDdnNKgW7c7IBMrP0AEm37HTu0LSOjP2OHXlvvlQ
8
+ GQIDAQAB
9
+ -----END PUBLIC KEY-----
@@ -0,0 +1,47 @@
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
+ const Pulsar = require('pulsar-client');
21
+
22
+ (async () => {
23
+ // Create a client
24
+ const client = new Pulsar.Client({
25
+ serviceUrl: 'pulsar://localhost:6650',
26
+ operationTimeoutSeconds: 30
27
+ });
28
+
29
+ // Create a consumer
30
+ const consumer = await client.subscribe({
31
+ topic: 'persistent://public/default/my-topic',
32
+ subscription: 'sub1',
33
+ subscriptionType: 'Shared',
34
+ ackTimeoutMs: 10000,
35
+ privateKeyPath: "./certificate/private-key.client-rsa.pem"
36
+ });
37
+
38
+ // Receive messages
39
+ for (let i = 0; i < 10; i += 1) {
40
+ const msg = await consumer.receive();
41
+ console.log(msg.getData().toString());
42
+ consumer.acknowledge(msg);
43
+ }
44
+
45
+ await consumer.close();
46
+ await client.close();
47
+ })();
@@ -0,0 +1,50 @@
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
+ const Pulsar = require('pulsar-client');
21
+
22
+ (async () => {
23
+ // Create a client
24
+ const client = new Pulsar.Client({
25
+ serviceUrl: 'pulsar://localhost:6650',
26
+ operationTimeoutSeconds: 30,
27
+ });
28
+
29
+ // Create a producer
30
+ const producer = await client.createProducer({
31
+ topic: 'persistent://public/default/my-topic',
32
+ sendTimeoutMs: 30000,
33
+ batchingEnabled: true,
34
+ publicKeyPath: "./certificate/public-key.client-rsa.pem",
35
+ encryptionKey: "encryption-key"
36
+ });
37
+
38
+ // Send messages
39
+ for (let i = 0; i < 10; i += 1) {
40
+ const msg = `my-message-${i}`;
41
+ producer.send({
42
+ data: Buffer.from(msg),
43
+ });
44
+ console.log(`Sent message: ${msg}`);
45
+ }
46
+ await producer.flush();
47
+
48
+ await producer.close();
49
+ await client.close();
50
+ })();
@@ -0,0 +1,37 @@
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
+ const Pulsar = require('pulsar-client');
21
+
22
+ (async () => {
23
+ // Create a client
24
+ const client = new Pulsar.Client({
25
+ serviceUrl: 'pulsar://localhost:6650',
26
+ operationTimeoutSeconds: 30,
27
+ });
28
+
29
+ // Create a reader
30
+ const reader = await client.createReader({
31
+ topic: 'persistent://public/default/my-topic',
32
+ startMessageId: Pulsar.MessageId.latest(),
33
+ listener: (msg, reader) => {
34
+ console.log(msg.getData().toString());
35
+ },
36
+ });
37
+ })();
package/index.d.ts CHANGED
@@ -56,6 +56,9 @@ export interface ProducerConfig {
56
56
  batchingMaxPublishDelayMs?: number;
57
57
  batchingMaxMessages?: number;
58
58
  properties?: { [key: string]: string };
59
+ publicKeyPath?: string;
60
+ encryptionKey?: string;
61
+ cryptoFailureAction?: ProducerCryptoFailureAction;
59
62
  }
60
63
 
61
64
  export class Producer {
@@ -64,6 +67,7 @@ export class Producer {
64
67
  close(): Promise<null>;
65
68
  getProducerName(): string;
66
69
  getTopic(): string;
70
+ isConnected(): boolean;
67
71
  }
68
72
 
69
73
  export interface ConsumerConfig {
@@ -81,6 +85,8 @@ export interface ConsumerConfig {
81
85
  properties?: { [key: string]: string };
82
86
  listener?: (message: Message, consumer: Consumer) => void;
83
87
  readCompacted?: boolean;
88
+ privateKeyPath?: string;
89
+ cryptoFailureAction?: ConsumerCryptoFailureAction;
84
90
  }
85
91
 
86
92
  export class Consumer {
@@ -91,6 +97,7 @@ export class Consumer {
91
97
  negativeAcknowledgeId(messageId: MessageId): void;
92
98
  acknowledgeCumulative(message: Message): void;
93
99
  acknowledgeCumulativeId(messageId: MessageId): void;
100
+ isConnected(): boolean;
94
101
  close(): Promise<null>;
95
102
  unsubscribe(): Promise<null>;
96
103
  }
@@ -102,11 +109,13 @@ export interface ReaderConfig {
102
109
  readerName?: string;
103
110
  subscriptionRolePrefix?: string;
104
111
  readCompacted?: boolean;
112
+ listener?: (message: Message, reader: Reader) => void;
105
113
  }
106
114
 
107
115
  export class Reader {
108
116
  readNext(timeout?: number): Promise<Message>;
109
117
  hasNext(): boolean;
118
+ isConnected(): boolean;
110
119
  close(): Promise<null>;
111
120
  }
112
121
 
@@ -116,9 +125,11 @@ export interface ProducerMessage {
116
125
  eventTimestamp?: number;
117
126
  sequenceId?: number;
118
127
  partitionKey?: string;
128
+ orderingKey?: string;
119
129
  replicationClusters?: string[];
120
130
  deliverAfter?: number;
121
131
  deliverAt?: number;
132
+ disableReplication?: boolean;
122
133
  }
123
134
 
124
135
  export class Message {
@@ -187,6 +198,10 @@ export type CompressionType =
187
198
  'ZSTD' |
188
199
  'SNAPPY';
189
200
 
201
+ export type ProducerCryptoFailureAction =
202
+ 'FAIL' |
203
+ 'SEND';
204
+
190
205
  export type SubscriptionType =
191
206
  'Exclusive' |
192
207
  'Shared' |
@@ -196,3 +211,8 @@ export type SubscriptionType =
196
211
  export type InitialPosition =
197
212
  'Latest' |
198
213
  'Earliest';
214
+
215
+ export type ConsumerCryptoFailureAction =
216
+ 'FAIL' |
217
+ 'DISCARD' |
218
+ 'CONSUME';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulsar-client",
3
- "version": "1.3.1",
3
+ "version": "1.4.1-rc.1",
4
4
  "description": "Pulsar Node.js client",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -42,10 +42,10 @@
42
42
  "eslint-config-airbnb-base": "^14.2.0",
43
43
  "eslint-plugin-import": "^2.22.0",
44
44
  "eslint-plugin-jest": "^24.3.6",
45
- "grunt": "^1.3.0",
45
+ "grunt": "^1.4.1",
46
46
  "grunt-license-report": "^0.0.8",
47
47
  "hdr-histogram-js": "^2.0.1",
48
- "jest": "^26.4.2",
48
+ "jest": "^27.2.4",
49
49
  "license-check-and-add": "^2.3.6",
50
50
  "lodash": "^4.17.21",
51
51
  "typescript": "^4.1.3"
@@ -53,7 +53,7 @@
53
53
  "dependencies": {
54
54
  "bindings": "^1.5.0",
55
55
  "node-addon-api": "^3.0.0",
56
- "node-gyp": "^7.1.0",
56
+ "node-gyp": "^8.2.0",
57
57
  "node-pre-gyp": "^0.15.0"
58
58
  },
59
59
  "binary": {
@@ -1 +1 @@
1
- 2.7.0
1
+ 2.8.0
package/src/Consumer.cc CHANGED
@@ -40,6 +40,7 @@ void Consumer::Init(Napi::Env env, Napi::Object exports) {
40
40
  InstanceMethod("negativeAcknowledgeId", &Consumer::NegativeAcknowledgeId),
41
41
  InstanceMethod("acknowledgeCumulative", &Consumer::AcknowledgeCumulative),
42
42
  InstanceMethod("acknowledgeCumulativeId", &Consumer::AcknowledgeCumulativeId),
43
+ InstanceMethod("isConnected", &Consumer::IsConnected),
43
44
  InstanceMethod("close", &Consumer::Close),
44
45
  InstanceMethod("unsubscribe", &Consumer::Unsubscribe),
45
46
  });
@@ -287,6 +288,11 @@ void Consumer::AcknowledgeCumulativeId(const Napi::CallbackInfo &info) {
287
288
  NULL);
288
289
  }
289
290
 
291
+ Napi::Value Consumer::IsConnected(const Napi::CallbackInfo &info) {
292
+ Napi::Env env = info.Env();
293
+ return Napi::Boolean::New(env, pulsar_consumer_is_connected(this->wrapper->cConsumer));
294
+ }
295
+
290
296
  class ConsumerCloseWorker : public Napi::AsyncWorker {
291
297
  public:
292
298
  ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer,
package/src/Consumer.h CHANGED
@@ -48,6 +48,7 @@ class Consumer : public Napi::ObjectWrap<Consumer> {
48
48
  void NegativeAcknowledgeId(const Napi::CallbackInfo &info);
49
49
  void AcknowledgeCumulative(const Napi::CallbackInfo &info);
50
50
  void AcknowledgeCumulativeId(const Napi::CallbackInfo &info);
51
+ Napi::Value IsConnected(const Napi::CallbackInfo &info);
51
52
  Napi::Value Close(const Napi::CallbackInfo &info);
52
53
  Napi::Value Unsubscribe(const Napi::CallbackInfo &info);
53
54
  };
@@ -38,6 +38,8 @@ static const std::string CFG_CONSUMER_NAME = "consumerName";
38
38
  static const std::string CFG_PROPS = "properties";
39
39
  static const std::string CFG_LISTENER = "listener";
40
40
  static const std::string CFG_READ_COMPACTED = "readCompacted";
41
+ static const std::string CFG_PRIVATE_KEY_PATH = "privateKeyPath";
42
+ static const std::string CFG_CRYPTO_FAILURE_ACTION = "cryptoFailureAction";
41
43
 
42
44
  static const std::map<std::string, pulsar_consumer_type> SUBSCRIPTION_TYPE = {
43
45
  {"Exclusive", pulsar_ConsumerExclusive},
@@ -48,6 +50,12 @@ static const std::map<std::string, pulsar_consumer_type> SUBSCRIPTION_TYPE = {
48
50
  static const std::map<std::string, initial_position> INIT_POSITION = {
49
51
  {"Latest", initial_position_latest}, {"Earliest", initial_position_earliest}};
50
52
 
53
+ static const std::map<std::string, pulsar_consumer_crypto_failure_action> CONSUMER_CRYPTO_FAILURE_ACTION = {
54
+ {"FAIL", pulsar_ConsumerFail},
55
+ {"DISCARD", pulsar_ConsumerDiscard},
56
+ {"CONSUME", pulsar_ConsumerConsume},
57
+ };
58
+
51
59
  void FinalizeListenerCallback(Napi::Env env, ListenerCallback *cb, void *) { delete cb; }
52
60
 
53
61
  ConsumerConfig::ConsumerConfig(const Napi::Object &consumerConfig,
@@ -163,6 +171,21 @@ ConsumerConfig::ConsumerConfig(const Napi::Object &consumerConfig,
163
171
  pulsar_consumer_set_read_compacted(this->cConsumerConfig, 1);
164
172
  }
165
173
  }
174
+
175
+ if (consumerConfig.Has(CFG_PRIVATE_KEY_PATH) && consumerConfig.Get(CFG_PRIVATE_KEY_PATH).IsString()) {
176
+ std::string publicKeyPath = "";
177
+ std::string privateKeyPath = consumerConfig.Get(CFG_PRIVATE_KEY_PATH).ToString().Utf8Value();
178
+ pulsar_consumer_configuration_set_default_crypto_key_reader(this->cConsumerConfig, publicKeyPath.c_str(),
179
+ privateKeyPath.c_str());
180
+ if (consumerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
181
+ consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
182
+ std::string cryptoFailureAction = consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
183
+ if (CONSUMER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction)) {
184
+ pulsar_consumer_configuration_set_crypto_failure_action(
185
+ this->cConsumerConfig, CONSUMER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
186
+ }
187
+ }
188
+ }
166
189
  }
167
190
 
168
191
  ConsumerConfig::~ConsumerConfig() {
package/src/Message.cc CHANGED
@@ -29,6 +29,8 @@ static const std::string CFG_PARTITION_KEY = "partitionKey";
29
29
  static const std::string CFG_REPL_CLUSTERS = "replicationClusters";
30
30
  static const std::string CFG_DELIVER_AFTER = "deliverAfter";
31
31
  static const std::string CFG_DELIVER_AT = "deliverAt";
32
+ static const std::string CFG_DISABLE_REPLICATION = "disableReplication";
33
+ static const std::string CFG_ORDERING_KEY = "orderingKey";
32
34
 
33
35
  Napi::FunctionReference Message::constructor;
34
36
 
@@ -206,6 +208,19 @@ pulsar_message_t *Message::BuildMessage(Napi::Object conf) {
206
208
  Napi::Number deliverAt = conf.Get(CFG_DELIVER_AT).ToNumber();
207
209
  pulsar_message_set_deliver_at(cMessage, deliverAt.Int64Value());
208
210
  }
211
+
212
+ if (conf.Has(CFG_DISABLE_REPLICATION) && conf.Get(CFG_DISABLE_REPLICATION).IsBoolean()) {
213
+ Napi::Boolean disableReplication = conf.Get(CFG_DISABLE_REPLICATION).ToBoolean();
214
+ if (disableReplication.Value()) {
215
+ pulsar_message_disable_replication(cMessage, 1);
216
+ }
217
+ }
218
+
219
+ if (conf.Has(CFG_ORDERING_KEY) && conf.Get(CFG_ORDERING_KEY).IsString()) {
220
+ Napi::String orderingKey = conf.Get(CFG_ORDERING_KEY).ToString();
221
+ pulsar_message_set_ordering_key(cMessage, orderingKey.Utf8Value().c_str());
222
+ }
223
+
209
224
  return cMessage;
210
225
  }
211
226
 
package/src/Producer.cc CHANGED
@@ -33,7 +33,8 @@ void Producer::Init(Napi::Env env, Napi::Object exports) {
33
33
  {InstanceMethod("send", &Producer::Send), InstanceMethod("flush", &Producer::Flush),
34
34
  InstanceMethod("close", &Producer::Close),
35
35
  InstanceMethod("getProducerName", &Producer::GetProducerName),
36
- InstanceMethod("getTopic", &Producer::GetTopic)});
36
+ InstanceMethod("getTopic", &Producer::GetTopic),
37
+ InstanceMethod("isConnected", &Producer::IsConnected)});
37
38
 
38
39
  constructor = Napi::Persistent(func);
39
40
  constructor.SuppressDestruct();
@@ -199,4 +200,9 @@ Napi::Value Producer::GetTopic(const Napi::CallbackInfo &info) {
199
200
  return Napi::String::New(env, pulsar_producer_get_topic(this->cProducer));
200
201
  }
201
202
 
203
+ Napi::Value Producer::IsConnected(const Napi::CallbackInfo &info) {
204
+ Napi::Env env = info.Env();
205
+ return Napi::Boolean::New(env, pulsar_producer_is_connected(this->cProducer));
206
+ }
207
+
202
208
  Producer::~Producer() { pulsar_producer_free(this->cProducer); }
package/src/Producer.h CHANGED
@@ -40,6 +40,7 @@ class Producer : public Napi::ObjectWrap<Producer> {
40
40
  Napi::Value Close(const Napi::CallbackInfo &info);
41
41
  Napi::Value GetProducerName(const Napi::CallbackInfo &info);
42
42
  Napi::Value GetTopic(const Napi::CallbackInfo &info);
43
+ Napi::Value IsConnected(const Napi::CallbackInfo &info);
43
44
  };
44
45
 
45
46
  #endif
@@ -34,6 +34,9 @@ static const std::string CFG_BATCH_ENABLED = "batchingEnabled";
34
34
  static const std::string CFG_BATCH_MAX_DELAY = "batchingMaxPublishDelayMs";
35
35
  static const std::string CFG_BATCH_MAX_MSG = "batchingMaxMessages";
36
36
  static const std::string CFG_PROPS = "properties";
37
+ static const std::string CFG_PUBLIC_KEY_PATH = "publicKeyPath";
38
+ static const std::string CFG_ENCRYPTION_KEY = "encryptionKey";
39
+ static const std::string CFG_CRYPTO_FAILURE_ACTION = "cryptoFailureAction";
37
40
 
38
41
  static const std::map<std::string, pulsar_partitions_routing_mode> MESSAGE_ROUTING_MODE = {
39
42
  {"UseSinglePartition", pulsar_UseSinglePartition},
@@ -53,6 +56,11 @@ static std::map<std::string, pulsar_compression_type> COMPRESSION_TYPE = {
53
56
  {"SNAPPY", pulsar_CompressionSNAPPY},
54
57
  };
55
58
 
59
+ static std::map<std::string, pulsar_producer_crypto_failure_action> PRODUCER_CRYPTO_FAILURE_ACTION = {
60
+ {"FAIL", pulsar_ProducerFail},
61
+ {"SEND", pulsar_ProducerSend},
62
+ };
63
+
56
64
  ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
57
65
  this->cProducerConfig = pulsar_producer_configuration_create();
58
66
 
@@ -153,6 +161,24 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
153
161
  value.Utf8Value().c_str());
154
162
  }
155
163
  }
164
+
165
+ if (producerConfig.Has(CFG_PUBLIC_KEY_PATH) && producerConfig.Get(CFG_PUBLIC_KEY_PATH).IsString()) {
166
+ std::string publicKeyPath = producerConfig.Get(CFG_PUBLIC_KEY_PATH).ToString().Utf8Value();
167
+ std::string privateKeyPath = "";
168
+ pulsar_producer_configuration_set_default_crypto_key_reader(this->cProducerConfig, publicKeyPath.c_str(),
169
+ privateKeyPath.c_str());
170
+ if (producerConfig.Has(CFG_ENCRYPTION_KEY) && producerConfig.Get(CFG_ENCRYPTION_KEY).IsString()) {
171
+ std::string encryptionKey = producerConfig.Get(CFG_ENCRYPTION_KEY).ToString().Utf8Value();
172
+ pulsar_producer_configuration_set_encryption_key(this->cProducerConfig, encryptionKey.c_str());
173
+ }
174
+ if (producerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
175
+ producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
176
+ std::string cryptoFailureAction = producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
177
+ if (PRODUCER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction))
178
+ pulsar_producer_configuration_set_crypto_failure_action(
179
+ this->cProducerConfig, PRODUCER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
180
+ }
181
+ }
156
182
  }
157
183
 
158
184
  ProducerConfig::~ProducerConfig() { pulsar_producer_configuration_free(this->cProducerConfig); }
package/src/Reader.cc CHANGED
@@ -22,6 +22,8 @@
22
22
  #include "ReaderConfig.h"
23
23
  #include <pulsar/c/result.h>
24
24
  #include <pulsar/c/reader.h>
25
+ #include <atomic>
26
+ #include <thread>
25
27
 
26
28
  Napi::FunctionReference Reader::constructor;
27
29
 
@@ -32,6 +34,7 @@ void Reader::Init(Napi::Env env, Napi::Object exports) {
32
34
  {
33
35
  InstanceMethod("readNext", &Reader::ReadNext),
34
36
  InstanceMethod("hasNext", &Reader::HasNext),
37
+ InstanceMethod("isConnected", &Reader::IsConnected),
35
38
  InstanceMethod("close", &Reader::Close),
36
39
  });
37
40
 
@@ -39,44 +42,86 @@ void Reader::Init(Napi::Env env, Napi::Object exports) {
39
42
  constructor.SuppressDestruct();
40
43
  }
41
44
 
42
- void Reader::SetCReader(pulsar_reader_t *cReader) { this->cReader = cReader; }
45
+ struct ReaderListenerProxyData {
46
+ pulsar_message_t *cMessage;
47
+ Reader *reader;
48
+
49
+ ReaderListenerProxyData(pulsar_message_t *cMessage, Reader *reader) : cMessage(cMessage), reader(reader) {}
50
+ };
51
+
52
+ void ReaderListenerProxy(Napi::Env env, Napi::Function jsCallback, ReaderListenerProxyData *data) {
53
+ Napi::Object msg = Message::NewInstance({}, data->cMessage);
54
+ Reader *reader = data->reader;
55
+ delete data;
56
+
57
+ jsCallback.Call({msg, reader->Value()});
58
+ }
43
59
 
44
- Reader::Reader(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Reader>(info) {}
60
+ void ReaderListener(pulsar_reader_t *cReader, pulsar_message_t *cMessage, void *ctx) {
61
+ ReaderListenerCallback *readerListenerCallback = (ReaderListenerCallback *)ctx;
62
+ Reader *reader = (Reader *)readerListenerCallback->reader;
63
+ if (readerListenerCallback->callback.Acquire() != napi_ok) {
64
+ return;
65
+ }
66
+ ReaderListenerProxyData *dataPtr = new ReaderListenerProxyData(cMessage, reader);
67
+ readerListenerCallback->callback.BlockingCall(dataPtr, ReaderListenerProxy);
68
+ readerListenerCallback->callback.Release();
69
+ }
70
+
71
+ void Reader::SetCReader(std::shared_ptr<CReaderWrapper> cReader) { this->wrapper = cReader; }
72
+ 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();
77
+
78
+ // Pass reader as argument
79
+ listener->reader = this;
80
+ }
81
+
82
+ this->listener = listener;
83
+ }
84
+
85
+ Reader::Reader(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Reader>(info), listener(nullptr) {}
45
86
 
46
87
  class ReaderNewInstanceWorker : public Napi::AsyncWorker {
47
88
  public:
48
89
  ReaderNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
49
- ReaderConfig *readerConfig)
90
+ ReaderConfig *readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper)
50
91
  : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
51
92
  deferred(deferred),
52
93
  cClient(cClient),
53
- readerConfig(readerConfig) {}
94
+ readerConfig(readerConfig),
95
+ readerWrapper(readerWrapper),
96
+ done(false) {}
54
97
  ~ReaderNewInstanceWorker() {}
55
98
  void Execute() {
56
99
  const std::string &topic = this->readerConfig->GetTopic();
57
100
  if (topic.empty()) {
58
- SetError(std::string("Topic is required and must be specified as a string when creating reader"));
101
+ std::string msg("Topic is required and must be specified as a string when creating reader");
102
+ SetError(msg);
59
103
  return;
60
104
  }
61
105
  if (this->readerConfig->GetCStartMessageId() == nullptr) {
62
- SetError(std::string(
63
- "StartMessageId is required and must be specified as a MessageId object when creating reader"));
106
+ std::string msg(
107
+ "StartMessageId is required and must be specified as a MessageId object when creating reader");
108
+ SetError(msg);
64
109
  return;
65
110
  }
66
111
 
67
- pulsar_result result =
68
- pulsar_client_create_reader(this->cClient, topic.c_str(), this->readerConfig->GetCStartMessageId(),
69
- this->readerConfig->GetCReaderConfig(), &(this->cReader));
70
- delete this->readerConfig;
71
- if (result != pulsar_result_Ok) {
72
- SetError(std::string("Failed to create reader: ") + pulsar_result_str(result));
73
- return;
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();
74
118
  }
75
119
  }
76
120
  void OnOK() {
77
121
  Napi::Object obj = Reader::constructor.New({});
78
122
  Reader *reader = Reader::Unwrap(obj);
79
- reader->SetCReader(this->cReader);
123
+ reader->SetCReader(this->readerWrapper);
124
+ reader->SetListenerCallback(this->listener);
80
125
  this->deferred.Resolve(obj);
81
126
  }
82
127
  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
@@ -84,15 +129,33 @@ class ReaderNewInstanceWorker : public Napi::AsyncWorker {
84
129
  private:
85
130
  Napi::Promise::Deferred deferred;
86
131
  pulsar_client_t *cClient;
87
- ReaderConfig *readerConfig;
88
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
+ 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();
144
+ }
145
+
146
+ delete worker->readerConfig;
147
+ worker->done = true;
148
+ }
89
149
  };
90
150
 
91
151
  Napi::Value Reader::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
92
152
  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
93
153
  Napi::Object config = info[0].As<Napi::Object>();
94
- ReaderConfig *readerConfig = new ReaderConfig(config);
95
- ReaderNewInstanceWorker *wk = new ReaderNewInstanceWorker(deferred, cClient, readerConfig);
154
+
155
+ std::shared_ptr<CReaderWrapper> readerWrapper = std::make_shared<CReaderWrapper>();
156
+
157
+ ReaderConfig *readerConfig = new ReaderConfig(config, readerWrapper, &ReaderListener);
158
+ ReaderNewInstanceWorker *wk = new ReaderNewInstanceWorker(deferred, cClient, readerConfig, readerWrapper);
96
159
  wk->Queue();
97
160
  return deferred.Promise();
98
161
  }
@@ -133,11 +196,12 @@ class ReaderReadNextWorker : public Napi::AsyncWorker {
133
196
  Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
134
197
  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
135
198
  if (info[0].IsUndefined()) {
136
- ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader);
199
+ ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->wrapper->cReader);
137
200
  wk->Queue();
138
201
  } else {
139
202
  Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
140
- ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader, timeout.Int64Value());
203
+ ReaderReadNextWorker *wk =
204
+ new ReaderReadNextWorker(deferred, this->wrapper->cReader, timeout.Int64Value());
141
205
  wk->Queue();
142
206
  }
143
207
  return deferred.Promise();
@@ -145,7 +209,7 @@ Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
145
209
 
146
210
  Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
147
211
  int value = 0;
148
- pulsar_result result = pulsar_reader_has_message_available(this->cReader, &value);
212
+ pulsar_result result = pulsar_reader_has_message_available(this->wrapper->cReader, &value);
149
213
  if (result != pulsar_result_Ok) {
150
214
  Napi::Error::New(info.Env(), "Failed to check if next message is available").ThrowAsJavaScriptException();
151
215
  return Napi::Boolean::New(info.Env(), false);
@@ -156,18 +220,27 @@ Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
156
220
  }
157
221
  }
158
222
 
223
+ Napi::Value Reader::IsConnected(const Napi::CallbackInfo &info) {
224
+ Napi::Env env = info.Env();
225
+ return Napi::Boolean::New(env, pulsar_reader_is_connected(this->wrapper->cReader));
226
+ }
227
+
159
228
  class ReaderCloseWorker : public Napi::AsyncWorker {
160
229
  public:
161
- ReaderCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader)
230
+ ReaderCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader, Reader *reader)
162
231
  : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
163
232
  deferred(deferred),
164
- cReader(cReader) {}
233
+ cReader(cReader),
234
+ reader(reader) {}
165
235
  ~ReaderCloseWorker() {}
166
236
  void Execute() {
167
237
  pulsar_result result = pulsar_reader_close(this->cReader);
168
238
  if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
169
239
  }
170
- void OnOK() { this->deferred.Resolve(Env().Null()); }
240
+ void OnOK() {
241
+ this->reader->Cleanup();
242
+ this->deferred.Resolve(Env().Null());
243
+ }
171
244
  void OnError(const Napi::Error &e) {
172
245
  this->deferred.Reject(
173
246
  Napi::Error::New(Env(), std::string("Failed to close reader: ") + e.Message()).Value());
@@ -176,13 +249,30 @@ class ReaderCloseWorker : public Napi::AsyncWorker {
176
249
  private:
177
250
  Napi::Promise::Deferred deferred;
178
251
  pulsar_reader_t *cReader;
252
+ Reader *reader;
179
253
  };
180
254
 
181
255
  Napi::Value Reader::Close(const Napi::CallbackInfo &info) {
182
256
  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
183
- ReaderCloseWorker *wk = new ReaderCloseWorker(deferred, this->cReader);
257
+ ReaderCloseWorker *wk = new ReaderCloseWorker(deferred, this->wrapper->cReader, this);
184
258
  wk->Queue();
185
259
  return deferred.Promise();
186
260
  }
187
261
 
188
- Reader::~Reader() { pulsar_reader_free(this->cReader); }
262
+ void Reader::Cleanup() {
263
+ if (this->listener) {
264
+ this->CleanupListener();
265
+ }
266
+ }
267
+
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
+ }
package/src/Reader.h CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  #include <napi.h>
24
24
  #include <pulsar/c/client.h>
25
+ #include "ReaderConfig.h"
25
26
 
26
27
  class Reader : public Napi::ObjectWrap<Reader> {
27
28
  public:
@@ -30,14 +31,19 @@ class Reader : public Napi::ObjectWrap<Reader> {
30
31
  static Napi::FunctionReference constructor;
31
32
  Reader(const Napi::CallbackInfo &info);
32
33
  ~Reader();
33
- void SetCReader(pulsar_reader_t *cReader);
34
+ void SetCReader(std::shared_ptr<CReaderWrapper> cReader);
35
+ void SetListenerCallback(ReaderListenerCallback *listener);
36
+ void Cleanup();
34
37
 
35
38
  private:
36
- pulsar_reader_t *cReader;
39
+ std::shared_ptr<CReaderWrapper> wrapper;
40
+ ReaderListenerCallback *listener;
37
41
 
38
42
  Napi::Value ReadNext(const Napi::CallbackInfo &info);
39
43
  Napi::Value HasNext(const Napi::CallbackInfo &info);
44
+ Napi::Value IsConnected(const Napi::CallbackInfo &info);
40
45
  Napi::Value Close(const Napi::CallbackInfo &info);
46
+ void CleanupListener();
41
47
  };
42
48
 
43
49
  #endif
@@ -27,8 +27,13 @@ static const std::string CFG_RECV_QUEUE = "receiverQueueSize";
27
27
  static const std::string CFG_READER_NAME = "readerName";
28
28
  static const std::string CFG_SUBSCRIPTION_ROLE_PREFIX = "subscriptionRolePrefix";
29
29
  static const std::string CFG_READ_COMPACTED = "readCompacted";
30
+ static const std::string CFG_LISTENER = "listener";
30
31
 
31
- ReaderConfig::ReaderConfig(const Napi::Object &readerConfig) : topic(""), cStartMessageId(NULL) {
32
+ void FinalizeListenerCallback(Napi::Env env, ReaderListenerCallback *cb, void *) { delete cb; }
33
+
34
+ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper,
35
+ pulsar_reader_listener readerListener)
36
+ : topic(""), cStartMessageId(NULL), listener(nullptr) {
32
37
  this->cReaderConfig = pulsar_reader_configuration_create();
33
38
 
34
39
  if (readerConfig.Has(CFG_TOPIC) && readerConfig.Get(CFG_TOPIC).IsString()) {
@@ -67,12 +72,40 @@ ReaderConfig::ReaderConfig(const Napi::Object &readerConfig) : topic(""), cStart
67
72
  pulsar_reader_configuration_set_read_compacted(this->cReaderConfig, 1);
68
73
  }
69
74
  }
75
+
76
+ if (readerConfig.Has(CFG_LISTENER) && readerConfig.Get(CFG_LISTENER).IsFunction()) {
77
+ this->listener = new ReaderListenerCallback();
78
+ Napi::ThreadSafeFunction callback = Napi::ThreadSafeFunction::New(
79
+ readerConfig.Env(), readerConfig.Get(CFG_LISTENER).As<Napi::Function>(), "Reader Listener Callback",
80
+ 1, 1, (void *)NULL, FinalizeListenerCallback, listener);
81
+ this->listener->callback = std::move(callback);
82
+ pulsar_reader_configuration_set_reader_listener(this->cReaderConfig, readerListener, this->listener);
83
+ }
70
84
  }
71
85
 
72
- ReaderConfig::~ReaderConfig() { pulsar_reader_configuration_free(this->cReaderConfig); }
86
+ ReaderConfig::~ReaderConfig() {
87
+ pulsar_reader_configuration_free(this->cReaderConfig);
88
+ if (this->listener) {
89
+ this->listener->callback.Release();
90
+ }
91
+ }
73
92
 
74
93
  pulsar_reader_configuration_t *ReaderConfig::GetCReaderConfig() { return this->cReaderConfig; }
75
94
 
76
95
  std::string ReaderConfig::GetTopic() { return this->topic; }
77
96
 
78
97
  pulsar_message_id_t *ReaderConfig::GetCStartMessageId() { return this->cStartMessageId; }
98
+
99
+ ReaderListenerCallback *ReaderConfig::GetListenerCallback() {
100
+ ReaderListenerCallback *cb = this->listener;
101
+ this->listener = nullptr;
102
+ return cb;
103
+ }
104
+
105
+ CReaderWrapper::CReaderWrapper() : cReader(nullptr) {}
106
+
107
+ CReaderWrapper::~CReaderWrapper() {
108
+ if (this->cReader) {
109
+ pulsar_reader_free(this->cReader);
110
+ }
111
+ }
@@ -24,19 +24,23 @@
24
24
  #include <pulsar/c/reader.h>
25
25
  #include <pulsar/c/reader_configuration.h>
26
26
  #include <pulsar/c/message_id.h>
27
+ #include "ReaderListener.h"
27
28
 
28
29
  class ReaderConfig {
29
30
  public:
30
- ReaderConfig(const Napi::Object &readerConfig);
31
+ ReaderConfig(const Napi::Object &readerConfig, std::shared_ptr<CReaderWrapper> readerWrapper,
32
+ pulsar_reader_listener readerListener);
31
33
  ~ReaderConfig();
32
34
  pulsar_reader_configuration_t *GetCReaderConfig();
33
35
  pulsar_message_id_t *GetCStartMessageId();
34
36
  std::string GetTopic();
37
+ ReaderListenerCallback *GetListenerCallback();
35
38
 
36
39
  private:
37
40
  std::string topic;
38
41
  pulsar_message_id_t *cStartMessageId;
39
42
  pulsar_reader_configuration_t *cReaderConfig;
43
+ ReaderListenerCallback *listener;
40
44
  };
41
45
 
42
46
  #endif
@@ -0,0 +1,39 @@
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 READER_LISTENER_H
21
+ #define READER_LISTENER_H
22
+
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
+
32
+ struct ReaderListenerCallback {
33
+ Napi::ThreadSafeFunction callback;
34
+
35
+ // Using reader as void* since the ReaderListenerCallback is shared between Config and Reader.
36
+ void *reader;
37
+ };
38
+
39
+ #endif
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEAtKWwgqdnTYrOCv+j1MkTWfSH0wCsHZZca9wAW3qP4uuhlBvn
3
+ b10JcFf5ZjzP9BSXK+tHmI8uoN368vEv6yhURHM4yuXqzCxzuAwkQSo39rzX8PGC
4
+ 7qdjCN7LDJ3MnqiBIrUsSaEP1wrNsB1kI+o9ER1e5O/uEPAotP933hHQ0J2hMEek
5
+ HqL7sBlJ98h6NmsicEaUkardk0TOXrlkjC+cMd8ZbGScPqI9M38bmn3OLxFTn1vt
6
+ hpvnXLvCmG4M+6xtYtD+npcVPZw1i1R90fMs7ppZnRbv8Hc/DFdOKVQIgam6CDdn
7
+ NKgW7c7IBMrP0AEm37HTu0LSOjP2OHXlvvlQGQIDAQABAoIBAAaJFAi2C7u3cNrf
8
+ AstY9vVDLoLIvHFZlkBktjKZDYmVIsRb+hSCViwVUrWLL67R6+Iv4eg4DeTOAx00
9
+ 8pncXKgZTw2wIb1/QjR/Y/RjlaC8lkdmRWli7udMQCZVsyhuSjW6Pj7vr8YE4woj
10
+ FhNijxEGcf9wWrmMJrzdnTWQiXByo+eTvUQ9BPgPGrRjsMZmTkLyAVJff2DfxO5b
11
+ IWFDYDJcyYAMCIMQu7vys/I50ou6ilb1CO6QM6Z7KpPeOoVFPwtzbh8cf9xM8UNS
12
+ j6J/JmdWhgI34GS3NA68xTQ6PV7zjnhCc+iccm3JKyzGXwaApAZ+Eoce/9j4WKmu
13
+ 5B4ziR0CgYEA3l/9OHbl1zmyV+rRxWOIj/i2rTvHzwBnbnPJyuemL5VMFdpGodQ3
14
+ vwHvyQmcECRVRxmXojQ4QuPPHs3qp6wEEFPCWxChLSTxlUc85SOFHWU2O99jV7zI
15
+ 7+JOpDK/Mstsx9nHgXduJF+glTFtA3LH8Oqylzu2aFPsprwKuZf94Q8CgYEAz/Zx
16
+ akEG+PEMtP5YS28cX5XfjsIX/V26Fs6/sH16QjUIEddE5T4fCuokxCjSiwUcWhml
17
+ pHEJ5S5xp3VYRfISW3jRW3qstIH1tpZipB6+S0zTuJmLJbA3IiWEg2rtMt7X1uJv
18
+ A/bYOqe0hOPTuXuZdtVZ0nMTKk7GG8O6VkBI7FcCgYEAkDfCmscJgs7JahlBWHmX
19
+ zH9pwem+SPKjIc/4NB6N+dgikx2Pp05hpP/VihUwYIufvs/LNogVYNQrtHepUnrN
20
+ 2+TmbHbZgNSv1Ldxt82UfB7y0FutKu6lhmXHyNecho3Fi8sih0V0aiSWmYuHfrAH
21
+ GaiskEZKo1iiZvQXJIx9O2MCgYATBf0r9hTYMtyxtc6H3/sdd01C9thQ8gDy0yjP
22
+ 0Tqc0dMSJroDqmIWkoKYew9/bhFA4LW5TCnWkCAPbHmNtG4fdfbYwmkH/hdnA2y0
23
+ jKdlpfp8GXeUFAGHGx17FA3sqFvgKUh0eWEgRHUL7vdQMVFBgJS93o7zQM94fLgP
24
+ 6cOB8wKBgFcGV4GjI2Ww9cillaC554MvoSjf8B/+04kXzDOh8iYIIzO9EUil1jjK
25
+ Jvxp4hnLzTKWbux3MEWqurLkYas6GpKBjw+iNOCar6YdqWGVqM3RUx7PTUaZwkKx
26
+ UdP63IfY7iZCIT/QbyHQvIUe2MaiVnH+ulxdkK6Y5e7gxcbckIH4
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtKWwgqdnTYrOCv+j1MkT
3
+ WfSH0wCsHZZca9wAW3qP4uuhlBvnb10JcFf5ZjzP9BSXK+tHmI8uoN368vEv6yhU
4
+ RHM4yuXqzCxzuAwkQSo39rzX8PGC7qdjCN7LDJ3MnqiBIrUsSaEP1wrNsB1kI+o9
5
+ ER1e5O/uEPAotP933hHQ0J2hMEekHqL7sBlJ98h6NmsicEaUkardk0TOXrlkjC+c
6
+ Md8ZbGScPqI9M38bmn3OLxFTn1vthpvnXLvCmG4M+6xtYtD+npcVPZw1i1R90fMs
7
+ 7ppZnRbv8Hc/DFdOKVQIgam6CDdnNKgW7c7IBMrP0AEm37HTu0LSOjP2OHXlvvlQ
8
+ GQIDAQAB
9
+ -----END PUBLIC KEY-----
@@ -213,6 +213,56 @@ const Pulsar = require('../index.js');
213
213
  await client.close();
214
214
  });
215
215
 
216
+ test('Produce/Read Listener', async () => {
217
+ const client = new Pulsar.Client({
218
+ serviceUrl: 'pulsar://localhost:6650',
219
+ operationTimeoutSeconds: 30,
220
+ });
221
+
222
+ const topic = 'persistent://public/default/produce-read-listener';
223
+ const producer = await client.createProducer({
224
+ topic,
225
+ sendTimeoutMs: 30000,
226
+ batchingEnabled: true,
227
+ });
228
+ expect(producer).not.toBeNull();
229
+
230
+ let finish;
231
+ const results = [];
232
+ const finishPromise = new Promise((resolve) => {
233
+ finish = resolve;
234
+ });
235
+
236
+ const reader = await client.createReader({
237
+ topic,
238
+ startMessageId: Pulsar.MessageId.latest(),
239
+ listener: (message) => {
240
+ const data = message.getData().toString();
241
+ results.push(data);
242
+ if (results.length === 10) finish();
243
+ },
244
+ });
245
+
246
+ expect(reader).not.toBeNull();
247
+
248
+ const messages = [];
249
+ for (let i = 0; i < 10; i += 1) {
250
+ const msg = `my-message-${i}`;
251
+ producer.send({
252
+ data: Buffer.from(msg),
253
+ });
254
+ messages.push(msg);
255
+ }
256
+ await producer.flush();
257
+
258
+ await finishPromise;
259
+ expect(lodash.difference(messages, results)).toEqual([]);
260
+
261
+ await producer.close();
262
+ await reader.close();
263
+ await client.close();
264
+ });
265
+
216
266
  test('acknowledgeCumulative', async () => {
217
267
  const client = new Pulsar.Client({
218
268
  serviceUrl: 'pulsar://localhost:6650',
@@ -708,10 +758,94 @@ const Pulsar = require('../index.js');
708
758
  }
709
759
  expect(lodash.difference(messages, results)).toEqual([]);
710
760
  expect(lodash.difference(messageIds, resultIds)).toEqual([]);
761
+ await producer.close();
762
+ await consumer.close();
763
+ await client.close();
764
+ });
765
+ test('Basic produce and consume encryption', async () => {
766
+ const client = new Pulsar.Client({
767
+ serviceUrl: 'pulsar://localhost:6650',
768
+ operationTimeoutSeconds: 30,
769
+ });
711
770
 
771
+ const topic = 'persistent://public/default/encryption-produce-consume';
772
+ const producer = await client.createProducer({
773
+ topic,
774
+ sendTimeoutMs: 30000,
775
+ batchingEnabled: true,
776
+ publicKeyPath: `${__dirname}/certificate/public-key.client-rsa.pem`,
777
+ encryptionKey: 'encryption-key',
778
+ });
779
+
780
+ const consumer = await client.subscribe({
781
+ topic,
782
+ subscription: 'sub1',
783
+ subscriptionType: 'Shared',
784
+ ackTimeoutMs: 10000,
785
+ privateKeyPath: `${__dirname}/certificate/private-key.client-rsa.pem`,
786
+ });
787
+
788
+ const messages = [];
789
+ for (let i = 0; i < 10; i += 1) {
790
+ const msg = `my-message-${i}`;
791
+ producer.send({
792
+ data: Buffer.from(msg),
793
+ });
794
+ messages.push(msg);
795
+ }
796
+ await producer.flush();
797
+
798
+ const results = [];
799
+ for (let i = 0; i < 10; i += 1) {
800
+ const msg = await consumer.receive();
801
+ consumer.acknowledge(msg);
802
+ results.push(msg.getData().toString());
803
+ }
804
+ expect(lodash.difference(messages, results)).toEqual([]);
712
805
  await producer.close();
713
806
  await consumer.close();
714
807
  await client.close();
715
808
  });
809
+ test('Produce/Consume/Read/IsConnected', async () => {
810
+ const client = new Pulsar.Client({
811
+ serviceUrl: 'pulsar://localhost:6650',
812
+ operationTimeoutSeconds: 30,
813
+ });
814
+
815
+ const topic = 'persistent://public/default/produce-consume';
816
+ const producer = await client.createProducer({
817
+ topic,
818
+ sendTimeoutMs: 30000,
819
+ batchingEnabled: true,
820
+ });
821
+ expect(producer).not.toBeNull();
822
+ expect(producer.isConnected()).toEqual(true);
823
+
824
+ const consumer = await client.subscribe({
825
+ topic,
826
+ subscription: 'sub1',
827
+ ackTimeoutMs: 10000,
828
+ });
829
+ expect(consumer).not.toBeNull();
830
+ expect(consumer.isConnected()).toEqual(true);
831
+
832
+ const reader = await client.createReader({
833
+ topic,
834
+ startMessageId: Pulsar.MessageId.latest(),
835
+ });
836
+ expect(reader).not.toBeNull();
837
+ expect(reader.isConnected()).toEqual(true);
838
+
839
+ await producer.close();
840
+ expect(producer.isConnected()).toEqual(false);
841
+
842
+ await consumer.close();
843
+ expect(consumer.isConnected()).toEqual(false);
844
+
845
+ await reader.close();
846
+ expect(reader.isConnected()).toEqual(false);
847
+
848
+ await client.close();
849
+ });
716
850
  });
717
851
  })();
package/tstest.ts CHANGED
@@ -91,6 +91,9 @@ import Pulsar = require('./index');
91
91
  key1: 'value1',
92
92
  key2: 'value2',
93
93
  },
94
+ publicKeyPath: '/path/to/public.key',
95
+ encryptionKey: 'encryption-key',
96
+ cryptoFailureAction: 'FAIL',
94
97
  });
95
98
 
96
99
  const producer2: Pulsar.Producer = await client.createProducer({
@@ -98,6 +101,7 @@ import Pulsar = require('./index');
98
101
  messageRoutingMode: 'RoundRobinDistribution',
99
102
  hashingScheme: 'BoostHash',
100
103
  compressionType: 'LZ4',
104
+ cryptoFailureAction: 'SEND',
101
105
  });
102
106
 
103
107
  const producer3: Pulsar.Producer = await client.createProducer({
@@ -127,6 +131,8 @@ import Pulsar = require('./index');
127
131
  key2: 'value2',
128
132
  },
129
133
  readCompacted: false,
134
+ privateKeyPath: '/path/to/private.key',
135
+ cryptoFailureAction: 'FAIL',
130
136
  });
131
137
 
132
138
  const consumer2: Pulsar.Consumer = await client.subscribe({
@@ -134,6 +140,7 @@ import Pulsar = require('./index');
134
140
  subscription: 'my-sub2',
135
141
  subscriptionType: 'Shared',
136
142
  subscriptionInitialPosition: 'Earliest',
143
+ cryptoFailureAction: 'DISCARD',
137
144
  });
138
145
 
139
146
  const consumer3: Pulsar.Consumer = await client.subscribe({
@@ -142,6 +149,7 @@ import Pulsar = require('./index');
142
149
  subscriptionType: 'KeyShared',
143
150
  listener: (message: Pulsar.Message, consumer: Pulsar.Consumer) => {
144
151
  },
152
+ cryptoFailureAction: 'CONSUME',
145
153
  });
146
154
 
147
155
  const consumer4: Pulsar.Consumer = await client.subscribe({
@@ -164,8 +172,16 @@ import Pulsar = require('./index');
164
172
  startMessageId: Pulsar.MessageId.earliest(),
165
173
  });
166
174
 
175
+ const reader3: Pulsar.Reader = await client.createReader({
176
+ topic: 'persistent://public/default/my-topic',
177
+ startMessageId: Pulsar.MessageId.earliest(),
178
+ listener: (message: Pulsar.Message, reader: Pulsar.Reader) => {
179
+ },
180
+ });
181
+
167
182
  const producerName: string = producer1.getProducerName();
168
183
  const topicName1: string = producer1.getTopic();
184
+ const producerIsConnected: boolean = producer1.isConnected();
169
185
 
170
186
  const messageId1: Pulsar.MessageId = await producer1.send({
171
187
  data: Buffer.from('my-message'),
@@ -176,12 +192,14 @@ import Pulsar = require('./index');
176
192
  eventTimestamp: Date.now(),
177
193
  sequenceId: 10,
178
194
  partitionKey: 'key1',
195
+ orderingKey: 'orderingKey1',
179
196
  replicationClusters: [
180
197
  'cluster1',
181
198
  'cluster2',
182
199
  ],
183
200
  deliverAfter: 30000,
184
201
  deliverAt: Date.now() + 30000,
202
+ disableReplication: false,
185
203
  });
186
204
 
187
205
  const messageIdString: string = messageId1.toString();
@@ -190,6 +208,7 @@ import Pulsar = require('./index');
190
208
 
191
209
  const message1: Pulsar.Message = await consumer1.receive();
192
210
  const message2: Pulsar.Message = await consumer2.receive(1000);
211
+ const consumerIsConnected: boolean = consumer1.isConnected();
193
212
 
194
213
  consumer1.negativeAcknowledge(message1);
195
214
  consumer1.negativeAcknowledgeId(messageId1);
@@ -210,6 +229,7 @@ import Pulsar = require('./index');
210
229
  const message3: Pulsar.Message = await reader1.readNext();
211
230
  const message4: Pulsar.Message = await reader2.readNext(1000);
212
231
  const hasNext: boolean = reader1.hasNext();
232
+ const readerIsConnected: boolean = reader1.isConnected();
213
233
 
214
234
  await producer1.flush();
215
235
  await producer1.close();
@@ -222,6 +242,7 @@ import Pulsar = require('./index');
222
242
  await consumer4.close();
223
243
  await reader1.close();
224
244
  await reader2.close();
245
+ await reader3.close();
225
246
  await client.close();
226
247
  })();
227
248