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.
- package/examples/certificate/private-key.client-rsa.pem +27 -0
- package/examples/certificate/public-key.client-rsa.pem +9 -0
- package/examples/encryption-consumer.js +47 -0
- package/examples/encryption-producer.js +50 -0
- package/examples/reader_listener.js +37 -0
- package/index.d.ts +20 -0
- package/package.json +4 -4
- package/pulsar-version.txt +1 -1
- package/src/Consumer.cc +6 -0
- package/src/Consumer.h +1 -0
- package/src/ConsumerConfig.cc +23 -0
- package/src/Message.cc +15 -0
- package/src/Producer.cc +7 -1
- package/src/Producer.h +1 -0
- package/src/ProducerConfig.cc +26 -0
- package/src/Reader.cc +116 -26
- package/src/Reader.h +8 -2
- package/src/ReaderConfig.cc +35 -2
- package/src/ReaderConfig.h +5 -1
- package/src/ReaderListener.h +39 -0
- package/tests/certificate/private-key.client-rsa.pem +27 -0
- package/tests/certificate/public-key.client-rsa.pem +9 -0
- package/tests/end_to_end.test.js +134 -0
- package/tstest.ts +21 -0
|
@@ -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
|
+
"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.
|
|
45
|
+
"grunt": "^1.4.1",
|
|
46
46
|
"grunt-license-report": "^0.0.8",
|
|
47
47
|
"hdr-histogram-js": "^2.0.1",
|
|
48
|
-
"jest": "^
|
|
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": "^
|
|
56
|
+
"node-gyp": "^8.2.0",
|
|
57
57
|
"node-pre-gyp": "^0.15.0"
|
|
58
58
|
},
|
|
59
59
|
"binary": {
|
package/pulsar-version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
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
|
};
|
package/src/ConsumerConfig.cc
CHANGED
|
@@ -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
|
package/src/ProducerConfig.cc
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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->
|
|
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
|
-
|
|
95
|
-
|
|
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 =
|
|
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() {
|
|
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
|
|
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(
|
|
34
|
+
void SetCReader(std::shared_ptr<CReaderWrapper> cReader);
|
|
35
|
+
void SetListenerCallback(ReaderListenerCallback *listener);
|
|
36
|
+
void Cleanup();
|
|
34
37
|
|
|
35
38
|
private:
|
|
36
|
-
|
|
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
|
package/src/ReaderConfig.cc
CHANGED
|
@@ -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
|
-
|
|
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() {
|
|
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
|
+
}
|
package/src/ReaderConfig.h
CHANGED
|
@@ -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-----
|
package/tests/end_to_end.test.js
CHANGED
|
@@ -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
|
|