1use crate::error::Error;
2use crate::types::*;
3
4pub struct FakeCloud {
6 base_url: String,
7 client: reqwest::Client,
8}
9
10impl FakeCloud {
11 pub fn new(base_url: &str) -> Self {
13 Self {
14 base_url: base_url.trim_end_matches('/').to_string(),
15 client: reqwest::Client::new(),
16 }
17 }
18
19 pub async fn health(&self) -> Result<HealthResponse, Error> {
23 let resp = self
24 .client
25 .get(format!("{}/_fakecloud/health", self.base_url))
26 .send()
27 .await?;
28 Self::parse(resp).await
29 }
30
31 pub async fn reset(&self) -> Result<ResetResponse, Error> {
33 let resp = self
34 .client
35 .post(format!("{}/_reset", self.base_url))
36 .send()
37 .await?;
38 Self::parse(resp).await
39 }
40
41 pub async fn reset_service(&self, service: &str) -> Result<ResetServiceResponse, Error> {
43 let resp = self
44 .client
45 .post(format!("{}/_fakecloud/reset/{}", self.base_url, service))
46 .send()
47 .await?;
48 Self::parse(resp).await
49 }
50
51 pub fn lambda(&self) -> LambdaClient<'_> {
54 LambdaClient { fc: self }
55 }
56
57 pub fn ses(&self) -> SesClient<'_> {
58 SesClient { fc: self }
59 }
60
61 pub fn sns(&self) -> SnsClient<'_> {
62 SnsClient { fc: self }
63 }
64
65 pub fn sqs(&self) -> SqsClient<'_> {
66 SqsClient { fc: self }
67 }
68
69 pub fn events(&self) -> EventsClient<'_> {
70 EventsClient { fc: self }
71 }
72
73 pub fn s3(&self) -> S3Client<'_> {
74 S3Client { fc: self }
75 }
76
77 pub fn dynamodb(&self) -> DynamoDbClient<'_> {
78 DynamoDbClient { fc: self }
79 }
80
81 pub fn secretsmanager(&self) -> SecretsManagerClient<'_> {
82 SecretsManagerClient { fc: self }
83 }
84
85 pub fn cognito(&self) -> CognitoClient<'_> {
86 CognitoClient { fc: self }
87 }
88
89 pub fn rds(&self) -> RdsClient<'_> {
90 RdsClient { fc: self }
91 }
92
93 pub fn elasticache(&self) -> ElastiCacheClient<'_> {
94 ElastiCacheClient { fc: self }
95 }
96
97 pub fn apigatewayv2(&self) -> ApiGatewayV2Client<'_> {
98 ApiGatewayV2Client { fc: self }
99 }
100
101 pub fn stepfunctions(&self) -> StepFunctionsClient<'_> {
102 StepFunctionsClient { fc: self }
103 }
104
105 pub fn bedrock(&self) -> BedrockClient<'_> {
106 BedrockClient { fc: self }
107 }
108
109 async fn parse<T: serde::de::DeserializeOwned>(resp: reqwest::Response) -> Result<T, Error> {
112 let status = resp.status().as_u16();
113 if !resp.status().is_success() {
114 let body = resp.text().await.unwrap_or_default();
115 return Err(Error::Api { status, body });
116 }
117 Ok(resp.json::<T>().await?)
118 }
119}
120
121pub struct RdsClient<'a> {
124 fc: &'a FakeCloud,
125}
126
127impl RdsClient<'_> {
128 pub async fn get_instances(&self) -> Result<RdsInstancesResponse, Error> {
130 let resp = self
131 .fc
132 .client
133 .get(format!("{}/_fakecloud/rds/instances", self.fc.base_url))
134 .send()
135 .await?;
136 FakeCloud::parse(resp).await
137 }
138}
139
140pub struct ElastiCacheClient<'a> {
143 fc: &'a FakeCloud,
144}
145
146impl ElastiCacheClient<'_> {
147 pub async fn get_clusters(&self) -> Result<ElastiCacheClustersResponse, Error> {
149 let resp = self
150 .fc
151 .client
152 .get(format!(
153 "{}/_fakecloud/elasticache/clusters",
154 self.fc.base_url
155 ))
156 .send()
157 .await?;
158 FakeCloud::parse(resp).await
159 }
160
161 pub async fn get_replication_groups(
163 &self,
164 ) -> Result<ElastiCacheReplicationGroupsResponse, Error> {
165 let resp = self
166 .fc
167 .client
168 .get(format!(
169 "{}/_fakecloud/elasticache/replication-groups",
170 self.fc.base_url
171 ))
172 .send()
173 .await?;
174 FakeCloud::parse(resp).await
175 }
176
177 pub async fn get_serverless_caches(
179 &self,
180 ) -> Result<ElastiCacheServerlessCachesResponse, Error> {
181 let resp = self
182 .fc
183 .client
184 .get(format!(
185 "{}/_fakecloud/elasticache/serverless-caches",
186 self.fc.base_url
187 ))
188 .send()
189 .await?;
190 FakeCloud::parse(resp).await
191 }
192}
193
194pub struct LambdaClient<'a> {
197 fc: &'a FakeCloud,
198}
199
200impl LambdaClient<'_> {
201 pub async fn get_invocations(&self) -> Result<LambdaInvocationsResponse, Error> {
203 let resp = self
204 .fc
205 .client
206 .get(format!(
207 "{}/_fakecloud/lambda/invocations",
208 self.fc.base_url
209 ))
210 .send()
211 .await?;
212 FakeCloud::parse(resp).await
213 }
214
215 pub async fn get_warm_containers(&self) -> Result<WarmContainersResponse, Error> {
217 let resp = self
218 .fc
219 .client
220 .get(format!(
221 "{}/_fakecloud/lambda/warm-containers",
222 self.fc.base_url
223 ))
224 .send()
225 .await?;
226 FakeCloud::parse(resp).await
227 }
228
229 pub async fn evict_container(
231 &self,
232 function_name: &str,
233 ) -> Result<EvictContainerResponse, Error> {
234 let resp = self
235 .fc
236 .client
237 .post(format!(
238 "{}/_fakecloud/lambda/{}/evict-container",
239 self.fc.base_url, function_name
240 ))
241 .send()
242 .await?;
243 FakeCloud::parse(resp).await
244 }
245}
246
247pub struct SesClient<'a> {
250 fc: &'a FakeCloud,
251}
252
253impl SesClient<'_> {
254 pub async fn get_emails(&self) -> Result<SesEmailsResponse, Error> {
256 let resp = self
257 .fc
258 .client
259 .get(format!("{}/_fakecloud/ses/emails", self.fc.base_url))
260 .send()
261 .await?;
262 FakeCloud::parse(resp).await
263 }
264
265 pub async fn simulate_inbound(
267 &self,
268 req: &InboundEmailRequest,
269 ) -> Result<InboundEmailResponse, Error> {
270 let resp = self
271 .fc
272 .client
273 .post(format!("{}/_fakecloud/ses/inbound", self.fc.base_url))
274 .json(req)
275 .send()
276 .await?;
277 FakeCloud::parse(resp).await
278 }
279}
280
281pub struct SnsClient<'a> {
284 fc: &'a FakeCloud,
285}
286
287impl SnsClient<'_> {
288 pub async fn get_messages(&self) -> Result<SnsMessagesResponse, Error> {
290 let resp = self
291 .fc
292 .client
293 .get(format!("{}/_fakecloud/sns/messages", self.fc.base_url))
294 .send()
295 .await?;
296 FakeCloud::parse(resp).await
297 }
298
299 pub async fn get_pending_confirmations(&self) -> Result<PendingConfirmationsResponse, Error> {
301 let resp = self
302 .fc
303 .client
304 .get(format!(
305 "{}/_fakecloud/sns/pending-confirmations",
306 self.fc.base_url
307 ))
308 .send()
309 .await?;
310 FakeCloud::parse(resp).await
311 }
312
313 pub async fn confirm_subscription(
315 &self,
316 req: &ConfirmSubscriptionRequest,
317 ) -> Result<ConfirmSubscriptionResponse, Error> {
318 let resp = self
319 .fc
320 .client
321 .post(format!(
322 "{}/_fakecloud/sns/confirm-subscription",
323 self.fc.base_url
324 ))
325 .json(req)
326 .send()
327 .await?;
328 FakeCloud::parse(resp).await
329 }
330}
331
332pub struct SqsClient<'a> {
335 fc: &'a FakeCloud,
336}
337
338impl SqsClient<'_> {
339 pub async fn get_messages(&self) -> Result<SqsMessagesResponse, Error> {
341 let resp = self
342 .fc
343 .client
344 .get(format!("{}/_fakecloud/sqs/messages", self.fc.base_url))
345 .send()
346 .await?;
347 FakeCloud::parse(resp).await
348 }
349
350 pub async fn tick_expiration(&self) -> Result<ExpirationTickResponse, Error> {
352 let resp = self
353 .fc
354 .client
355 .post(format!(
356 "{}/_fakecloud/sqs/expiration-processor/tick",
357 self.fc.base_url
358 ))
359 .send()
360 .await?;
361 FakeCloud::parse(resp).await
362 }
363
364 pub async fn force_dlq(&self, queue_name: &str) -> Result<ForceDlqResponse, Error> {
366 let resp = self
367 .fc
368 .client
369 .post(format!(
370 "{}/_fakecloud/sqs/{}/force-dlq",
371 self.fc.base_url, queue_name
372 ))
373 .send()
374 .await?;
375 FakeCloud::parse(resp).await
376 }
377}
378
379pub struct EventsClient<'a> {
382 fc: &'a FakeCloud,
383}
384
385impl EventsClient<'_> {
386 pub async fn get_history(&self) -> Result<EventHistoryResponse, Error> {
388 let resp = self
389 .fc
390 .client
391 .get(format!("{}/_fakecloud/events/history", self.fc.base_url))
392 .send()
393 .await?;
394 FakeCloud::parse(resp).await
395 }
396
397 pub async fn fire_rule(&self, req: &FireRuleRequest) -> Result<FireRuleResponse, Error> {
399 let resp = self
400 .fc
401 .client
402 .post(format!("{}/_fakecloud/events/fire-rule", self.fc.base_url))
403 .json(req)
404 .send()
405 .await?;
406 FakeCloud::parse(resp).await
407 }
408}
409
410pub struct S3Client<'a> {
413 fc: &'a FakeCloud,
414}
415
416impl S3Client<'_> {
417 pub async fn get_notifications(&self) -> Result<S3NotificationsResponse, Error> {
419 let resp = self
420 .fc
421 .client
422 .get(format!("{}/_fakecloud/s3/notifications", self.fc.base_url))
423 .send()
424 .await?;
425 FakeCloud::parse(resp).await
426 }
427
428 pub async fn tick_lifecycle(&self) -> Result<LifecycleTickResponse, Error> {
430 let resp = self
431 .fc
432 .client
433 .post(format!(
434 "{}/_fakecloud/s3/lifecycle-processor/tick",
435 self.fc.base_url
436 ))
437 .send()
438 .await?;
439 FakeCloud::parse(resp).await
440 }
441}
442
443pub struct DynamoDbClient<'a> {
446 fc: &'a FakeCloud,
447}
448
449impl DynamoDbClient<'_> {
450 pub async fn tick_ttl(&self) -> Result<TtlTickResponse, Error> {
452 let resp = self
453 .fc
454 .client
455 .post(format!(
456 "{}/_fakecloud/dynamodb/ttl-processor/tick",
457 self.fc.base_url
458 ))
459 .send()
460 .await?;
461 FakeCloud::parse(resp).await
462 }
463}
464
465pub struct SecretsManagerClient<'a> {
468 fc: &'a FakeCloud,
469}
470
471impl SecretsManagerClient<'_> {
472 pub async fn tick_rotation(&self) -> Result<RotationTickResponse, Error> {
474 let resp = self
475 .fc
476 .client
477 .post(format!(
478 "{}/_fakecloud/secretsmanager/rotation-scheduler/tick",
479 self.fc.base_url
480 ))
481 .send()
482 .await?;
483 FakeCloud::parse(resp).await
484 }
485}
486
487pub struct CognitoClient<'a> {
490 fc: &'a FakeCloud,
491}
492
493impl CognitoClient<'_> {
494 pub async fn get_user_codes(
496 &self,
497 pool_id: &str,
498 username: &str,
499 ) -> Result<UserConfirmationCodes, Error> {
500 let resp = self
501 .fc
502 .client
503 .get(format!(
504 "{}/_fakecloud/cognito/confirmation-codes/{}/{}",
505 self.fc.base_url, pool_id, username
506 ))
507 .send()
508 .await?;
509 FakeCloud::parse(resp).await
510 }
511
512 pub async fn get_confirmation_codes(&self) -> Result<ConfirmationCodesResponse, Error> {
514 let resp = self
515 .fc
516 .client
517 .get(format!(
518 "{}/_fakecloud/cognito/confirmation-codes",
519 self.fc.base_url
520 ))
521 .send()
522 .await?;
523 FakeCloud::parse(resp).await
524 }
525
526 pub async fn confirm_user(
528 &self,
529 req: &ConfirmUserRequest,
530 ) -> Result<ConfirmUserResponse, Error> {
531 let resp = self
532 .fc
533 .client
534 .post(format!(
535 "{}/_fakecloud/cognito/confirm-user",
536 self.fc.base_url
537 ))
538 .json(req)
539 .send()
540 .await?;
541 let status = resp.status().as_u16();
542 let body: ConfirmUserResponse = resp.json().await?;
543 if status >= 400 {
544 return Err(Error::Api {
545 status,
546 body: body.error.unwrap_or_default(),
547 });
548 }
549 Ok(body)
550 }
551
552 pub async fn get_tokens(&self) -> Result<TokensResponse, Error> {
554 let resp = self
555 .fc
556 .client
557 .get(format!("{}/_fakecloud/cognito/tokens", self.fc.base_url))
558 .send()
559 .await?;
560 FakeCloud::parse(resp).await
561 }
562
563 pub async fn expire_tokens(
565 &self,
566 req: &ExpireTokensRequest,
567 ) -> Result<ExpireTokensResponse, Error> {
568 let resp = self
569 .fc
570 .client
571 .post(format!(
572 "{}/_fakecloud/cognito/expire-tokens",
573 self.fc.base_url
574 ))
575 .json(req)
576 .send()
577 .await?;
578 FakeCloud::parse(resp).await
579 }
580
581 pub async fn get_auth_events(&self) -> Result<AuthEventsResponse, Error> {
583 let resp = self
584 .fc
585 .client
586 .get(format!(
587 "{}/_fakecloud/cognito/auth-events",
588 self.fc.base_url
589 ))
590 .send()
591 .await?;
592 FakeCloud::parse(resp).await
593 }
594}
595
596pub struct ApiGatewayV2Client<'a> {
599 fc: &'a FakeCloud,
600}
601
602impl ApiGatewayV2Client<'_> {
603 pub async fn get_requests(&self) -> Result<ApiGatewayV2RequestsResponse, Error> {
605 let resp = self
606 .fc
607 .client
608 .get(format!(
609 "{}/_fakecloud/apigatewayv2/requests",
610 self.fc.base_url
611 ))
612 .send()
613 .await?;
614 FakeCloud::parse(resp).await
615 }
616}
617
618pub struct StepFunctionsClient<'a> {
621 fc: &'a FakeCloud,
622}
623
624impl StepFunctionsClient<'_> {
625 pub async fn get_executions(&self) -> Result<StepFunctionsExecutionsResponse, Error> {
627 let resp = self
628 .fc
629 .client
630 .get(format!(
631 "{}/_fakecloud/stepfunctions/executions",
632 self.fc.base_url
633 ))
634 .send()
635 .await?;
636 FakeCloud::parse(resp).await
637 }
638}
639
640pub struct BedrockClient<'a> {
643 fc: &'a FakeCloud,
644}
645
646impl BedrockClient<'_> {
647 pub async fn get_invocations(&self) -> Result<BedrockInvocationsResponse, Error> {
650 let resp = self
651 .fc
652 .client
653 .get(format!(
654 "{}/_fakecloud/bedrock/invocations",
655 self.fc.base_url
656 ))
657 .send()
658 .await?;
659 FakeCloud::parse(resp).await
660 }
661
662 pub async fn set_model_response(
664 &self,
665 model_id: &str,
666 response: &str,
667 ) -> Result<BedrockModelResponseConfig, Error> {
668 let resp = self
669 .fc
670 .client
671 .post(format!(
672 "{}/_fakecloud/bedrock/models/{}/response",
673 self.fc.base_url, model_id
674 ))
675 .header("content-type", "text/plain")
676 .body(response.to_string())
677 .send()
678 .await?;
679 FakeCloud::parse(resp).await
680 }
681
682 pub async fn set_response_rules(
684 &self,
685 model_id: &str,
686 rules: &[BedrockResponseRule],
687 ) -> Result<BedrockModelResponseConfig, Error> {
688 let body = serde_json::json!({ "rules": rules });
689 let resp = self
690 .fc
691 .client
692 .post(format!(
693 "{}/_fakecloud/bedrock/models/{}/responses",
694 self.fc.base_url, model_id
695 ))
696 .json(&body)
697 .send()
698 .await?;
699 FakeCloud::parse(resp).await
700 }
701
702 pub async fn clear_response_rules(
704 &self,
705 model_id: &str,
706 ) -> Result<BedrockModelResponseConfig, Error> {
707 let resp = self
708 .fc
709 .client
710 .delete(format!(
711 "{}/_fakecloud/bedrock/models/{}/responses",
712 self.fc.base_url, model_id
713 ))
714 .send()
715 .await?;
716 FakeCloud::parse(resp).await
717 }
718
719 pub async fn queue_fault(
721 &self,
722 rule: &BedrockFaultRule,
723 ) -> Result<BedrockStatusResponse, Error> {
724 let resp = self
725 .fc
726 .client
727 .post(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
728 .json(rule)
729 .send()
730 .await?;
731 FakeCloud::parse(resp).await
732 }
733
734 pub async fn get_faults(&self) -> Result<BedrockFaultsResponse, Error> {
736 let resp = self
737 .fc
738 .client
739 .get(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
740 .send()
741 .await?;
742 FakeCloud::parse(resp).await
743 }
744
745 pub async fn clear_faults(&self) -> Result<BedrockStatusResponse, Error> {
747 let resp = self
748 .fc
749 .client
750 .delete(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
751 .send()
752 .await?;
753 FakeCloud::parse(resp).await
754 }
755}