1pub mod config;
10mod connection;
11pub mod metadata;
12mod metrics;
13pub mod sink;
14pub mod source;
15
16pub use sink::RabbitMqSink;
17pub use source::RabbitMqSource;
18
19use std::sync::Arc;
20
21use arrow_schema::Schema;
22
23use crate::config::{ConfigKeySpec, ConnectorInfo};
24use crate::registry::ConnectorRegistry;
25
26pub fn register_rabbitmq_source(
32 registry: &ConnectorRegistry,
33) -> Result<(), crate::error::ConnectorError> {
34 let info = ConnectorInfo {
35 schema_capabilities: crate::schema::resolution::SchemaCapabilities {
36 built_in_formats: vec![
37 "raw".into(),
38 "bytes".into(),
39 #[cfg(feature = "fpml")]
40 "fpml".into(),
41 ],
42 ..crate::schema::resolution::SchemaCapabilities::declared(false)
43 },
44 name: "rabbitmq".to_string(),
45 display_name: "RabbitMQ Source".to_string(),
46 version: env!("CARGO_PKG_VERSION").to_string(),
47 is_source: true,
48 is_sink: false,
49 config_keys: source_config_keys(),
50 };
51 registry.register_source(
52 "rabbitmq",
53 info,
54 Arc::new(|registry| {
55 Ok(Box::new(RabbitMqSource::new(
56 Arc::new(Schema::empty()),
57 registry.map(Arc::as_ref),
58 )))
59 }),
60 )
61}
62
63pub fn register_rabbitmq_sink(
69 registry: &ConnectorRegistry,
70) -> Result<(), crate::error::ConnectorError> {
71 let info = ConnectorInfo {
72 schema_capabilities: crate::schema::resolution::SchemaCapabilities::declared(true),
73 name: "rabbitmq".to_string(),
74 display_name: "RabbitMQ Sink".to_string(),
75 version: env!("CARGO_PKG_VERSION").to_string(),
76 is_source: false,
77 is_sink: true,
78 config_keys: sink_config_keys(),
79 };
80 registry.register_sink(
81 "rabbitmq",
82 info,
83 Arc::new(|_config, registry| {
84 Ok(Box::new(RabbitMqSink::new(
85 Arc::new(Schema::empty()),
86 registry.map(Arc::as_ref),
87 )))
88 }),
89 )
90}
91
92fn connection_keys() -> Vec<ConfigKeySpec> {
93 use ConfigKeySpec as K;
94 vec![
95 K::required(
96 "url",
97 "AMQP URI(s), comma-separated failover endpoints of one broker or cluster: \
98 amqp[s]://[user:pass@]host[:port]/vhost[?heartbeat=&frame_max=&channel_max=\
99 &connection_timeout=&auth_mechanism=]",
100 ),
101 K::optional("username", "SASL user (instead of URI userinfo)", ""),
102 K::optional(
103 "password",
104 "SASL password; use a ${ENV} secret reference",
105 "",
106 ),
107 K::optional(
108 "connection.name",
109 "Client-provided connection name shown by the broker",
110 "",
111 ),
112 K::optional(
113 "connect.timeout.ms",
114 "Per-endpoint connect bound (or the URI connection_timeout, not both)",
115 "10000",
116 ),
117 K::optional(
118 "tls.ca.location",
119 "PEM CA bundle trusted in addition to the Web PKI roots (amqps)",
120 "",
121 ),
122 K::optional(
123 "tls.cert.location",
124 "PEM client certificate for mutual TLS (amqps)",
125 "",
126 ),
127 K::optional(
128 "tls.key.location",
129 "PKCS#8 PEM client key for mutual TLS (amqps)",
130 "",
131 ),
132 ]
133}
134
135fn source_config_keys() -> Vec<ConfigKeySpec> {
136 use ConfigKeySpec as K;
137 let mut keys = connection_keys();
138 keys.extend([
139 K::required("queue", "Classic or quorum queue to consume"),
140 K::optional(
141 "queue.declare",
142 "Declare the queue idempotently instead of requiring it to exist",
143 "false",
144 ),
145 K::optional("queue.durable", "Durable flag when declaring", "true"),
146 K::optional(
147 "queue.type",
148 "x-queue-type when declaring: classic | quorum",
149 "",
150 ),
151 K::optional(
152 "queue.arguments",
153 "JSON object of declaration-only x-arguments; prefer broker policies for mutable \
154 settings (TTL, dead-lettering, limits)",
155 "",
156 ),
157 K::optional(
158 "queue.bind.exchange",
159 "Exchange to bind the queue to at startup",
160 "",
161 ),
162 K::optional(
163 "queue.bind.routing.keys",
164 "Comma-separated binding keys",
165 "",
166 ),
167 K::optional(
168 "queue.bind.arguments",
169 "JSON object of binding arguments",
170 "",
171 ),
172 K::optional(
173 "prefetch.count",
174 "basic.qos prefetch: most unacknowledged deliveries; size it for one checkpoint \
175 interval of traffic",
176 "1000",
177 ),
178 K::optional(
179 "consumer.tag",
180 "Consumer tag (broker-generated when unset)",
181 "",
182 ),
183 K::optional("consumer.exclusive", "Exclusive consumer", "false"),
184 K::optional(
185 "consumer.arguments",
186 "JSON object of basic.consume arguments",
187 "",
188 ),
189 K::optional(
190 "max.message.bytes",
191 "Largest accepted message body",
192 "16777216",
193 ),
194 K::optional(
195 "max.batch.bytes",
196 "Payload bytes decoded per batch",
197 "33554432",
198 ),
199 K::optional(
200 "on.decode.error",
201 "fail | reject (basic.reject without requeue: dead-lettered by the queue's DLX, \
202 otherwise discarded)",
203 "fail",
204 ),
205 K::optional(
206 "metadata.columns",
207 "Comma-separated delivery metadata columns (exchange, routing_key, redelivered, \
208 delivery_count, message_id, correlation_id, timestamp, content_type, \
209 content_encoding, delivery_mode, priority, expiration, reply_to, type, user_id, \
210 app_id, headers)",
211 "",
212 ),
213 ]);
214 #[cfg(feature = "fpml")]
215 keys.extend(crate::fpml::message::config_keys());
216 keys
217}
218
219fn sink_config_keys() -> Vec<ConfigKeySpec> {
220 use ConfigKeySpec as K;
221 let mut keys = connection_keys();
222 keys.extend([
223 K::optional(
224 "exchange",
225 "Target exchange; empty is the default exchange",
226 "",
227 ),
228 K::optional(
229 "exchange.declare",
230 "Declare the exchange idempotently",
231 "false",
232 ),
233 K::optional("exchange.type", "Exchange type when declaring", ""),
234 K::optional("exchange.durable", "Durable flag when declaring", "true"),
235 K::optional("routing.key", "Literal routing key", ""),
236 K::optional(
237 "routing.key.column",
238 "Utf8 column holding each message's routing key",
239 "",
240 ),
241 K::optional(
242 "mandatory",
243 "Return unroutable messages; a return fails the write",
244 "true",
245 ),
246 K::optional("delivery.mode", "persistent | transient", "persistent"),
247 K::optional(
248 "content.type",
249 "content-type property (defaults by format)",
250 "",
251 ),
252 K::optional("content.encoding", "content-encoding property", ""),
253 K::optional(
254 "message.id.column",
255 "Utf8 column for the message-id property",
256 "",
257 ),
258 K::optional(
259 "correlation.id.column",
260 "Utf8 column for the correlation-id property",
261 "",
262 ),
263 K::optional("priority", "Literal priority property (0-255)", ""),
264 K::optional(
265 "priority.column",
266 "Integer column for the priority property",
267 "",
268 ),
269 K::optional(
270 "expiration.ms",
271 "Per-message TTL property in milliseconds",
272 "",
273 ),
274 K::optional("reply.to", "reply-to property", ""),
275 K::optional("type", "type property", ""),
276 K::optional("app.id", "app-id property", ""),
277 K::optional(
278 "header.columns",
279 "Comma-separated columns written as typed headers",
280 "",
281 ),
282 K::optional("max.pending", "Most publishes awaiting a confirm", "4096"),
283 K::optional(
284 "max.pending.bytes",
285 "Most payload bytes awaiting a confirm",
286 "67108864",
287 ),
288 K::optional("confirm.timeout.ms", "Bound for awaiting confirms", "30000"),
289 ]);
290 keys
291}