redis_cloud/connectivity/mod.rs
1//! Network connectivity and peering operations for Pro subscriptions
2//!
3//! This module manages advanced networking features for Redis Cloud Pro subscriptions,
4//! including VPC peering, AWS Transit Gateway attachments, GCP Private Service Connect,
5//! AWS `PrivateLink`, and other cloud-native networking integrations.
6//!
7//! # Supported Connectivity Types
8//!
9//! - **VPC Peering**: Direct peering between Redis Cloud VPC and your VPC
10//! - **Transit Gateway**: AWS Transit Gateway attachments for hub-and-spoke topologies
11//! - **Private Service Connect**: GCP Private Service Connect for private endpoints
12//! - **`PrivateLink`**: AWS `PrivateLink` for secure private connectivity
13//!
14//! # Module Organization
15//!
16//! The connectivity features are split into four specialized modules:
17//! - `vpc_peering` - VPC peering operations for AWS, GCP, and Azure
18//! - `psc` - Google Cloud Private Service Connect endpoints
19//! - `transit_gateway` - AWS Transit Gateway attachments
20//! - `private_link` - AWS `PrivateLink` connectivity
21
22pub mod private_link;
23pub mod psc;
24pub mod transit_gateway;
25pub mod vpc_peering;
26
27// Re-export handlers for convenience
28pub use private_link::PrivateLinkHandler;
29
30// Re-export PrivateLink types
31pub use private_link::{
32 PrincipalType, PrivateLinkActiveActiveConnectionsDisassociateRequest,
33 PrivateLinkAddPrincipalRequest, PrivateLinkConnectionDisassociate,
34 PrivateLinkConnectionsDisassociateRequest, PrivateLinkCreateRequest,
35 PrivateLinkRemovePrincipalRequest,
36};
37pub use psc::PscHandler;
38pub use transit_gateway::TransitGatewayHandler;
39pub use vpc_peering::VpcPeeringHandler;
40
41// Re-export types used by handlers
42pub use psc::PscEndpointUpdateRequest;
43pub use transit_gateway::{Cidr, TgwAttachmentRequest, TgwUpdateCidrsRequest};
44pub use vpc_peering::{
45 ActiveActiveVpcPeering, ActiveActiveVpcPeeringCreateRequest, ActiveActiveVpcPeeringList,
46 ActiveActiveVpcRegion, VpcCidr, VpcPeering, VpcPeeringCreateBaseRequest,
47 VpcPeeringCreateRequest, VpcPeeringUpdateAwsRequest, VpcPeeringUpdateRequest,
48};
49
50// For backward compatibility, provide a unified handler
51use crate::CloudClient;
52
53/// Unified connectivity handler combining VPC Peering, PSC, and Transit Gateway operations.
54///
55/// `ConnectivityHandler` is a backward-compatibility facade: each method
56/// delegates to the underlying specialized handler. New code should prefer
57/// the specialized handlers directly — they expose the full surface for their
58/// respective connectivity domains:
59///
60/// - [`VpcPeeringHandler`] — VPC peering for AWS, GCP, and Azure
61/// - [`PscHandler`] — Google Cloud Private Service Connect
62/// - [`TransitGatewayHandler`] — AWS Transit Gateway attachments
63///
64/// Errors returned from delegated calls propagate unchanged; see each backing
65/// handler's documentation for the full error surface.
66///
67/// # Example
68///
69/// ```rust,no_run
70/// use redis_cloud::{CloudClient, ConnectivityHandler};
71///
72/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
73/// let client = CloudClient::builder()
74/// .api_key("key")
75/// .api_secret("secret")
76/// .build()?;
77///
78/// let connectivity = ConnectivityHandler::new(client);
79/// let _vpc_task = connectivity.get_vpc_peering(123).await?;
80/// # Ok(())
81/// # }
82/// ```
83pub struct ConnectivityHandler {
84 /// VPC peering sub-handler. See [`VpcPeeringHandler`].
85 pub vpc_peering: VpcPeeringHandler,
86 /// Private Service Connect sub-handler. See [`PscHandler`].
87 pub psc: PscHandler,
88 /// Transit Gateway sub-handler. See [`TransitGatewayHandler`].
89 pub transit_gateway: TransitGatewayHandler,
90}
91
92impl ConnectivityHandler {
93 /// Construct a `ConnectivityHandler` wired to the supplied [`CloudClient`].
94 ///
95 /// Internally clones the client into each of the three sub-handlers, so
96 /// `client` can be dropped after construction if not needed elsewhere.
97 #[must_use]
98 pub fn new(client: CloudClient) -> Self {
99 Self {
100 vpc_peering: VpcPeeringHandler::new(client.clone()),
101 psc: PscHandler::new(client.clone()),
102 transit_gateway: TransitGatewayHandler::new(client),
103 }
104 }
105
106 // ========================================================================
107 // VPC Peering delegation methods
108 // ========================================================================
109
110 /// Get the VPC peering configuration for a Pro subscription.
111 ///
112 /// `GET /subscriptions/{subscriptionId}/peerings`. Delegates to
113 /// [`VpcPeeringHandler::get`].
114 ///
115 /// # Errors
116 ///
117 /// Propagates any [`crate::CloudError`] returned by the underlying call —
118 /// typically `NotFound` if the subscription has no peering configured,
119 /// `AuthenticationFailed` for bad credentials, or `InternalServerError`.
120 pub async fn get_vpc_peering(
121 &self,
122 subscription_id: i32,
123 ) -> crate::Result<crate::types::TaskStateUpdate> {
124 self.vpc_peering.get(subscription_id).await
125 }
126
127 /// Create a VPC peering on a Pro subscription.
128 ///
129 /// `POST /subscriptions/{subscriptionId}/peerings`. Delegates to
130 /// [`VpcPeeringHandler::create`]. Use
131 /// [`VpcPeeringCreateRequest::for_aws`] or
132 /// [`VpcPeeringCreateRequest::for_gcp`] to build a provider-targeted body
133 /// with the spec's required fields.
134 ///
135 /// # Errors
136 ///
137 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
138 /// responses. `BadRequest` is common when the body is missing
139 /// provider-required fields.
140 pub async fn create_vpc_peering(
141 &self,
142 subscription_id: i32,
143 request: &VpcPeeringCreateRequest,
144 ) -> crate::Result<crate::types::TaskStateUpdate> {
145 self.vpc_peering.create(subscription_id, request).await
146 }
147
148 /// Delete a VPC peering by its peering ID.
149 ///
150 /// `DELETE /subscriptions/{subscriptionId}/peerings/{peeringId}`.
151 /// Delegates to [`VpcPeeringHandler::delete`]. The deletion is asynchronous;
152 /// the returned [`TaskStateUpdate`](crate::types::TaskStateUpdate) carries a
153 /// `task_id` that can be polled to completion.
154 ///
155 /// # Errors
156 ///
157 /// Returns [`crate::CloudError`] for non-2xx responses; `NotFound` if the
158 /// peering does not exist.
159 pub async fn delete_vpc_peering(
160 &self,
161 subscription_id: i32,
162 peering_id: i32,
163 ) -> crate::Result<crate::types::TaskStateUpdate> {
164 self.vpc_peering.delete(subscription_id, peering_id).await
165 }
166
167 /// Update a VPC peering's CIDR list.
168 ///
169 /// `PUT /subscriptions/{subscriptionId}/peerings/{peeringId}`. Translates
170 /// the AWS-only [`VpcPeeringUpdateAwsRequest`] into the underlying
171 /// [`VpcPeeringCreateRequest`] shape and delegates to
172 /// [`VpcPeeringHandler::update`].
173 ///
174 /// # Errors
175 ///
176 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
177 /// responses.
178 pub async fn update_vpc_peering(
179 &self,
180 subscription_id: i32,
181 peering_id: i32,
182 request: &VpcPeeringUpdateAwsRequest,
183 ) -> crate::Result<crate::types::TaskStateUpdate> {
184 let create_request = VpcPeeringCreateRequest {
185 provider: None,
186 command_type: request.command_type.clone(),
187 vpc_cidr: request.vpc_cidr.clone(),
188 vpc_cidrs: request.vpc_cidrs.clone(),
189 ..Default::default()
190 };
191 self.vpc_peering
192 .update(subscription_id, peering_id, &create_request)
193 .await
194 }
195
196 // ========================================================================
197 // PSC (Private Service Connect) delegation methods
198 // ========================================================================
199
200 /// Get the Private Service Connect service for a Pro subscription.
201 ///
202 /// `GET /subscriptions/{subscriptionId}/private-service-connect`.
203 /// Delegates to [`PscHandler::get_service`].
204 ///
205 /// # Errors
206 ///
207 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
208 /// responses. `NotFound` is returned when no PSC service is configured.
209 pub async fn get_psc_service(
210 &self,
211 subscription_id: i32,
212 ) -> crate::Result<crate::types::TaskStateUpdate> {
213 self.psc.get_service(subscription_id).await
214 }
215
216 /// Create a Private Service Connect service on a Pro subscription.
217 ///
218 /// `POST /subscriptions/{subscriptionId}/private-service-connect`.
219 /// Delegates to [`PscHandler::create_service`].
220 ///
221 /// # Errors
222 ///
223 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
224 /// responses.
225 pub async fn create_psc_service(
226 &self,
227 subscription_id: i32,
228 ) -> crate::Result<crate::types::TaskStateUpdate> {
229 self.psc.create_service(subscription_id).await
230 }
231
232 /// Delete the Private Service Connect service for a Pro subscription.
233 ///
234 /// `DELETE /subscriptions/{subscriptionId}/private-service-connect`.
235 /// Delegates to [`PscHandler::delete_service`]. The deletion is
236 /// asynchronous; the returned
237 /// [`TaskStateUpdate`](crate::types::TaskStateUpdate) carries a `task_id`
238 /// that can be polled to completion.
239 ///
240 /// # Errors
241 ///
242 /// Returns [`crate::CloudError`] for non-2xx responses.
243 pub async fn delete_psc_service(
244 &self,
245 subscription_id: i32,
246 ) -> crate::Result<crate::types::TaskStateUpdate> {
247 self.psc.delete_service(subscription_id).await
248 }
249
250 /// Create a PSC endpoint under the subscription's PSC service.
251 ///
252 /// `POST /subscriptions/{subscriptionId}/private-service-connect/.../endpoints`.
253 /// Delegates to [`PscHandler::create_endpoint`].
254 ///
255 /// # Errors
256 ///
257 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
258 /// responses; `BadRequest` if the endpoint payload is malformed.
259 pub async fn create_psc_endpoint(
260 &self,
261 subscription_id: i32,
262 psc_service_id: i32,
263 request: &PscEndpointUpdateRequest,
264 ) -> crate::Result<crate::types::TaskStateUpdate> {
265 self.psc
266 .create_endpoint(subscription_id, psc_service_id, request)
267 .await
268 }
269
270 // ========================================================================
271 // Transit Gateway delegation methods
272 // ========================================================================
273
274 /// List Transit Gateway attachments for a Pro subscription.
275 ///
276 /// `GET /subscriptions/{subscriptionId}/transitGateways`. Delegates to
277 /// [`TransitGatewayHandler::get_attachments`].
278 ///
279 /// # Errors
280 ///
281 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
282 /// responses.
283 pub async fn get_tgws(
284 &self,
285 subscription_id: i32,
286 ) -> crate::Result<crate::types::TaskStateUpdate> {
287 self.transit_gateway.get_attachments(subscription_id).await
288 }
289
290 /// Create a Transit Gateway attachment for the given TGW ID.
291 ///
292 /// `POST /subscriptions/{subscriptionId}/transitGateways/{tgwId}`.
293 /// Delegates to [`TransitGatewayHandler::create_attachment_with_id`].
294 ///
295 /// # Errors
296 ///
297 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
298 /// responses; `BadRequest` if `tgw_id` is not in a valid AWS format.
299 pub async fn create_tgw_attachment(
300 &self,
301 subscription_id: i32,
302 tgw_id: &str,
303 ) -> crate::Result<crate::types::TaskStateUpdate> {
304 self.transit_gateway
305 .create_attachment_with_id(subscription_id, tgw_id)
306 .await
307 }
308
309 /// Delete a Transit Gateway attachment by attachment ID.
310 ///
311 /// `DELETE /subscriptions/{subscriptionId}/transitGateways/{tgwId}`.
312 /// Delegates to [`TransitGatewayHandler::delete_attachment`]. The deletion
313 /// is asynchronous; the returned
314 /// [`TaskStateUpdate`](crate::types::TaskStateUpdate) carries a `task_id`
315 /// that can be polled to completion.
316 ///
317 /// # Errors
318 ///
319 /// Returns [`crate::CloudError`] for non-2xx responses; `NotFound` if
320 /// the attachment does not exist.
321 pub async fn delete_tgw_attachment(
322 &self,
323 subscription_id: i32,
324 attachment_id: i32,
325 ) -> crate::Result<crate::types::TaskStateUpdate> {
326 self.transit_gateway
327 .delete_attachment(subscription_id, attachment_id.to_string())
328 .await
329 }
330
331 /// Update the CIDR list on a Transit Gateway attachment.
332 ///
333 /// `PUT /subscriptions/{subscriptionId}/transitGateways/{tgwId}/attachment`.
334 /// Translates [`TgwUpdateCidrsRequest`] (which carries CIDR-with-status
335 /// entries) into the [`TgwAttachmentRequest`] the underlying handler
336 /// expects and delegates to
337 /// [`TransitGatewayHandler::update_attachment_cidrs`]. Only the
338 /// `cidr_address` portion of each CIDR entry is forwarded.
339 ///
340 /// # Errors
341 ///
342 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
343 /// responses; `BadRequest` if any CIDR string is not in valid form.
344 pub async fn update_tgw_cidrs(
345 &self,
346 subscription_id: i32,
347 attachment_id: &str,
348 request: &TgwUpdateCidrsRequest,
349 ) -> crate::Result<crate::types::TaskStateUpdate> {
350 let attachment_request = TgwAttachmentRequest {
351 aws_account_id: None,
352 tgw_id: None,
353 cidrs: request.cidrs.as_ref().map(|cidrs| {
354 cidrs
355 .iter()
356 .filter_map(|c| c.cidr_address.clone())
357 .collect()
358 }),
359 };
360 self.transit_gateway
361 .update_attachment_cidrs(
362 subscription_id,
363 attachment_id.to_string(),
364 &attachment_request,
365 )
366 .await
367 }
368
369 // ========================================================================
370 // Backward-compatibility shims
371 // ========================================================================
372
373 /// Update a PSC endpoint by endpoint ID. Backward-compatibility wrapper
374 /// over [`PscHandler::update_endpoint`].
375 ///
376 /// `PUT /subscriptions/{subscriptionId}/private-service-connect/.../endpoints/{endpointId}`.
377 ///
378 /// # Errors
379 ///
380 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
381 /// responses.
382 pub async fn update_psc_service_endpoint(
383 &self,
384 subscription_id: i32,
385 psc_service_id: i32,
386 endpoint_id: i32,
387 request: &PscEndpointUpdateRequest,
388 ) -> crate::Result<crate::types::TaskStateUpdate> {
389 self.psc
390 .update_endpoint(subscription_id, psc_service_id, endpoint_id, request)
391 .await
392 }
393
394 /// Alias for [`Self::update_tgw_cidrs`] retained for backward
395 /// compatibility. New code should call `update_tgw_cidrs` directly.
396 ///
397 /// # Errors
398 ///
399 /// Forwards any [`crate::CloudError`] from the wrapped call.
400 pub async fn update_tgw_attachment_cidrs(
401 &self,
402 subscription_id: i32,
403 attachment_id: &str,
404 request: &TgwUpdateCidrsRequest,
405 ) -> crate::Result<crate::types::TaskStateUpdate> {
406 self.update_tgw_cidrs(subscription_id, attachment_id, request)
407 .await
408 }
409
410 // ========================================================================
411 // Simplified aliases
412 // ========================================================================
413
414 /// List Transit Gateway attachments for a Pro subscription (simplified).
415 ///
416 /// Preferred alias for [`get_tgws`](Self::get_tgws), whose name is
417 /// ambiguous. `get_tgws` is retained as a backward-compatibility shim.
418 ///
419 /// `GET /subscriptions/{subscriptionId}/transitGateways`.
420 ///
421 /// # Errors
422 ///
423 /// Returns [`crate::CloudError`] for transport, auth, or 4xx/5xx
424 /// responses.
425 ///
426 /// # Example
427 ///
428 /// ```no_run
429 /// use redis_cloud::{CloudClient, ConnectivityHandler};
430 ///
431 /// # async fn example() -> redis_cloud::Result<()> {
432 /// let client = CloudClient::builder()
433 /// .api_key("your-api-key")
434 /// .api_secret("your-api-secret")
435 /// .build()?;
436 ///
437 /// let connectivity = ConnectivityHandler::new(client);
438 /// let attachments = connectivity.list_tgw_attachments(123).await?;
439 /// # Ok(())
440 /// # }
441 /// ```
442 pub async fn list_tgw_attachments(
443 &self,
444 subscription_id: i32,
445 ) -> crate::Result<crate::types::TaskStateUpdate> {
446 self.get_tgws(subscription_id).await
447 }
448}