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 create_admin(
46 &self,
47 account_id: &str,
48 user_name: &str,
49 ) -> Result<CreateAdminResponse, Error> {
50 let resp = self
51 .client
52 .post(format!("{}/_fakecloud/iam/create-admin", self.base_url))
53 .json(&CreateAdminRequest {
54 account_id: account_id.to_string(),
55 user_name: user_name.to_string(),
56 })
57 .send()
58 .await?;
59 Self::parse(resp).await
60 }
61
62 pub async fn reset_service(&self, service: &str) -> Result<ResetServiceResponse, Error> {
64 let resp = self
65 .client
66 .post(format!("{}/_fakecloud/reset/{}", self.base_url, service))
67 .send()
68 .await?;
69 Self::parse(resp).await
70 }
71
72 pub async fn reset_service_for_account(
74 &self,
75 service: &str,
76 account_id: &str,
77 ) -> Result<ResetServiceResponse, Error> {
78 let resp = self
79 .client
80 .post(format!(
81 "{}/_fakecloud/reset/{}/{}",
82 self.base_url, service, account_id
83 ))
84 .send()
85 .await?;
86 Self::parse(resp).await
87 }
88
89 pub fn lambda(&self) -> LambdaClient<'_> {
92 LambdaClient { fc: self }
93 }
94
95 pub fn ses(&self) -> SesClient<'_> {
96 SesClient { fc: self }
97 }
98
99 pub fn sns(&self) -> SnsClient<'_> {
100 SnsClient { fc: self }
101 }
102
103 pub fn sqs(&self) -> SqsClient<'_> {
104 SqsClient { fc: self }
105 }
106
107 pub fn events(&self) -> EventsClient<'_> {
108 EventsClient { fc: self }
109 }
110
111 pub fn s3(&self) -> S3Client<'_> {
112 S3Client { fc: self }
113 }
114
115 pub fn dynamodb(&self) -> DynamoDbClient<'_> {
116 DynamoDbClient { fc: self }
117 }
118
119 pub fn secretsmanager(&self) -> SecretsManagerClient<'_> {
120 SecretsManagerClient { fc: self }
121 }
122
123 pub fn cognito(&self) -> CognitoClient<'_> {
124 CognitoClient { fc: self }
125 }
126
127 pub fn rds(&self) -> RdsClient<'_> {
128 RdsClient { fc: self }
129 }
130
131 pub fn elasticache(&self) -> ElastiCacheClient<'_> {
132 ElastiCacheClient { fc: self }
133 }
134
135 pub fn apigatewayv2(&self) -> ApiGatewayV2Client<'_> {
136 ApiGatewayV2Client { fc: self }
137 }
138
139 pub fn stepfunctions(&self) -> StepFunctionsClient<'_> {
140 StepFunctionsClient { fc: self }
141 }
142
143 pub fn bedrock(&self) -> BedrockClient<'_> {
144 BedrockClient { fc: self }
145 }
146
147 async fn parse<T: serde::de::DeserializeOwned>(resp: reqwest::Response) -> Result<T, Error> {
150 let status = resp.status().as_u16();
151 if !resp.status().is_success() {
152 let body = resp.text().await.unwrap_or_default();
153 return Err(Error::Api { status, body });
154 }
155 Ok(resp.json::<T>().await?)
156 }
157}
158
159pub struct RdsClient<'a> {
162 fc: &'a FakeCloud,
163}
164
165impl RdsClient<'_> {
166 pub async fn get_instances(&self) -> Result<RdsInstancesResponse, Error> {
168 let resp = self
169 .fc
170 .client
171 .get(format!("{}/_fakecloud/rds/instances", self.fc.base_url))
172 .send()
173 .await?;
174 FakeCloud::parse(resp).await
175 }
176}
177
178pub struct ElastiCacheClient<'a> {
181 fc: &'a FakeCloud,
182}
183
184impl ElastiCacheClient<'_> {
185 pub async fn get_clusters(&self) -> Result<ElastiCacheClustersResponse, Error> {
187 let resp = self
188 .fc
189 .client
190 .get(format!(
191 "{}/_fakecloud/elasticache/clusters",
192 self.fc.base_url
193 ))
194 .send()
195 .await?;
196 FakeCloud::parse(resp).await
197 }
198
199 pub async fn get_replication_groups(
201 &self,
202 ) -> Result<ElastiCacheReplicationGroupsResponse, Error> {
203 let resp = self
204 .fc
205 .client
206 .get(format!(
207 "{}/_fakecloud/elasticache/replication-groups",
208 self.fc.base_url
209 ))
210 .send()
211 .await?;
212 FakeCloud::parse(resp).await
213 }
214
215 pub async fn get_serverless_caches(
217 &self,
218 ) -> Result<ElastiCacheServerlessCachesResponse, Error> {
219 let resp = self
220 .fc
221 .client
222 .get(format!(
223 "{}/_fakecloud/elasticache/serverless-caches",
224 self.fc.base_url
225 ))
226 .send()
227 .await?;
228 FakeCloud::parse(resp).await
229 }
230}
231
232pub struct LambdaClient<'a> {
235 fc: &'a FakeCloud,
236}
237
238impl LambdaClient<'_> {
239 pub async fn get_invocations(&self) -> Result<LambdaInvocationsResponse, Error> {
241 let resp = self
242 .fc
243 .client
244 .get(format!(
245 "{}/_fakecloud/lambda/invocations",
246 self.fc.base_url
247 ))
248 .send()
249 .await?;
250 FakeCloud::parse(resp).await
251 }
252
253 pub async fn get_warm_containers(&self) -> Result<WarmContainersResponse, Error> {
255 let resp = self
256 .fc
257 .client
258 .get(format!(
259 "{}/_fakecloud/lambda/warm-containers",
260 self.fc.base_url
261 ))
262 .send()
263 .await?;
264 FakeCloud::parse(resp).await
265 }
266
267 pub async fn evict_container(
269 &self,
270 function_name: &str,
271 ) -> Result<EvictContainerResponse, Error> {
272 let resp = self
273 .fc
274 .client
275 .post(format!(
276 "{}/_fakecloud/lambda/{}/evict-container",
277 self.fc.base_url, function_name
278 ))
279 .send()
280 .await?;
281 FakeCloud::parse(resp).await
282 }
283}
284
285pub struct SesClient<'a> {
288 fc: &'a FakeCloud,
289}
290
291impl SesClient<'_> {
292 pub async fn get_emails(&self) -> Result<SesEmailsResponse, Error> {
294 let resp = self
295 .fc
296 .client
297 .get(format!("{}/_fakecloud/ses/emails", self.fc.base_url))
298 .send()
299 .await?;
300 FakeCloud::parse(resp).await
301 }
302
303 pub async fn simulate_inbound(
305 &self,
306 req: &InboundEmailRequest,
307 ) -> Result<InboundEmailResponse, Error> {
308 let resp = self
309 .fc
310 .client
311 .post(format!("{}/_fakecloud/ses/inbound", self.fc.base_url))
312 .json(req)
313 .send()
314 .await?;
315 FakeCloud::parse(resp).await
316 }
317}
318
319pub struct SnsClient<'a> {
322 fc: &'a FakeCloud,
323}
324
325impl SnsClient<'_> {
326 pub async fn get_messages(&self) -> Result<SnsMessagesResponse, Error> {
328 let resp = self
329 .fc
330 .client
331 .get(format!("{}/_fakecloud/sns/messages", self.fc.base_url))
332 .send()
333 .await?;
334 FakeCloud::parse(resp).await
335 }
336
337 pub async fn get_pending_confirmations(&self) -> Result<PendingConfirmationsResponse, Error> {
339 let resp = self
340 .fc
341 .client
342 .get(format!(
343 "{}/_fakecloud/sns/pending-confirmations",
344 self.fc.base_url
345 ))
346 .send()
347 .await?;
348 FakeCloud::parse(resp).await
349 }
350
351 pub async fn confirm_subscription(
353 &self,
354 req: &ConfirmSubscriptionRequest,
355 ) -> Result<ConfirmSubscriptionResponse, Error> {
356 let resp = self
357 .fc
358 .client
359 .post(format!(
360 "{}/_fakecloud/sns/confirm-subscription",
361 self.fc.base_url
362 ))
363 .json(req)
364 .send()
365 .await?;
366 FakeCloud::parse(resp).await
367 }
368}
369
370pub struct SqsClient<'a> {
373 fc: &'a FakeCloud,
374}
375
376impl SqsClient<'_> {
377 pub async fn get_messages(&self) -> Result<SqsMessagesResponse, Error> {
379 let resp = self
380 .fc
381 .client
382 .get(format!("{}/_fakecloud/sqs/messages", self.fc.base_url))
383 .send()
384 .await?;
385 FakeCloud::parse(resp).await
386 }
387
388 pub async fn tick_expiration(&self) -> Result<ExpirationTickResponse, Error> {
390 let resp = self
391 .fc
392 .client
393 .post(format!(
394 "{}/_fakecloud/sqs/expiration-processor/tick",
395 self.fc.base_url
396 ))
397 .send()
398 .await?;
399 FakeCloud::parse(resp).await
400 }
401
402 pub async fn force_dlq(&self, queue_name: &str) -> Result<ForceDlqResponse, Error> {
404 let resp = self
405 .fc
406 .client
407 .post(format!(
408 "{}/_fakecloud/sqs/{}/force-dlq",
409 self.fc.base_url, queue_name
410 ))
411 .send()
412 .await?;
413 FakeCloud::parse(resp).await
414 }
415}
416
417pub struct EventsClient<'a> {
420 fc: &'a FakeCloud,
421}
422
423impl EventsClient<'_> {
424 pub async fn get_history(&self) -> Result<EventHistoryResponse, Error> {
426 let resp = self
427 .fc
428 .client
429 .get(format!("{}/_fakecloud/events/history", self.fc.base_url))
430 .send()
431 .await?;
432 FakeCloud::parse(resp).await
433 }
434
435 pub async fn fire_rule(&self, req: &FireRuleRequest) -> Result<FireRuleResponse, Error> {
437 let resp = self
438 .fc
439 .client
440 .post(format!("{}/_fakecloud/events/fire-rule", self.fc.base_url))
441 .json(req)
442 .send()
443 .await?;
444 FakeCloud::parse(resp).await
445 }
446}
447
448pub struct S3Client<'a> {
451 fc: &'a FakeCloud,
452}
453
454impl S3Client<'_> {
455 pub async fn get_notifications(&self) -> Result<S3NotificationsResponse, Error> {
457 let resp = self
458 .fc
459 .client
460 .get(format!("{}/_fakecloud/s3/notifications", self.fc.base_url))
461 .send()
462 .await?;
463 FakeCloud::parse(resp).await
464 }
465
466 pub async fn tick_lifecycle(&self) -> Result<LifecycleTickResponse, Error> {
468 let resp = self
469 .fc
470 .client
471 .post(format!(
472 "{}/_fakecloud/s3/lifecycle-processor/tick",
473 self.fc.base_url
474 ))
475 .send()
476 .await?;
477 FakeCloud::parse(resp).await
478 }
479}
480
481pub struct DynamoDbClient<'a> {
484 fc: &'a FakeCloud,
485}
486
487impl DynamoDbClient<'_> {
488 pub async fn tick_ttl(&self) -> Result<TtlTickResponse, Error> {
490 let resp = self
491 .fc
492 .client
493 .post(format!(
494 "{}/_fakecloud/dynamodb/ttl-processor/tick",
495 self.fc.base_url
496 ))
497 .send()
498 .await?;
499 FakeCloud::parse(resp).await
500 }
501}
502
503pub struct SecretsManagerClient<'a> {
506 fc: &'a FakeCloud,
507}
508
509impl SecretsManagerClient<'_> {
510 pub async fn tick_rotation(&self) -> Result<RotationTickResponse, Error> {
512 let resp = self
513 .fc
514 .client
515 .post(format!(
516 "{}/_fakecloud/secretsmanager/rotation-scheduler/tick",
517 self.fc.base_url
518 ))
519 .send()
520 .await?;
521 FakeCloud::parse(resp).await
522 }
523}
524
525pub struct CognitoClient<'a> {
528 fc: &'a FakeCloud,
529}
530
531impl CognitoClient<'_> {
532 pub async fn get_user_codes(
534 &self,
535 pool_id: &str,
536 username: &str,
537 ) -> Result<UserConfirmationCodes, Error> {
538 let resp = self
539 .fc
540 .client
541 .get(format!(
542 "{}/_fakecloud/cognito/confirmation-codes/{}/{}",
543 self.fc.base_url, pool_id, username
544 ))
545 .send()
546 .await?;
547 FakeCloud::parse(resp).await
548 }
549
550 pub async fn get_confirmation_codes(&self) -> Result<ConfirmationCodesResponse, Error> {
552 let resp = self
553 .fc
554 .client
555 .get(format!(
556 "{}/_fakecloud/cognito/confirmation-codes",
557 self.fc.base_url
558 ))
559 .send()
560 .await?;
561 FakeCloud::parse(resp).await
562 }
563
564 pub async fn confirm_user(
566 &self,
567 req: &ConfirmUserRequest,
568 ) -> Result<ConfirmUserResponse, Error> {
569 let resp = self
570 .fc
571 .client
572 .post(format!(
573 "{}/_fakecloud/cognito/confirm-user",
574 self.fc.base_url
575 ))
576 .json(req)
577 .send()
578 .await?;
579 let status = resp.status().as_u16();
580 let body: ConfirmUserResponse = resp.json().await?;
581 if status >= 400 {
582 return Err(Error::Api {
583 status,
584 body: body.error.unwrap_or_default(),
585 });
586 }
587 Ok(body)
588 }
589
590 pub async fn get_tokens(&self) -> Result<TokensResponse, Error> {
592 let resp = self
593 .fc
594 .client
595 .get(format!("{}/_fakecloud/cognito/tokens", self.fc.base_url))
596 .send()
597 .await?;
598 FakeCloud::parse(resp).await
599 }
600
601 pub async fn expire_tokens(
603 &self,
604 req: &ExpireTokensRequest,
605 ) -> Result<ExpireTokensResponse, Error> {
606 let resp = self
607 .fc
608 .client
609 .post(format!(
610 "{}/_fakecloud/cognito/expire-tokens",
611 self.fc.base_url
612 ))
613 .json(req)
614 .send()
615 .await?;
616 FakeCloud::parse(resp).await
617 }
618
619 pub async fn get_auth_events(&self) -> Result<AuthEventsResponse, Error> {
621 let resp = self
622 .fc
623 .client
624 .get(format!(
625 "{}/_fakecloud/cognito/auth-events",
626 self.fc.base_url
627 ))
628 .send()
629 .await?;
630 FakeCloud::parse(resp).await
631 }
632}
633
634pub struct ApiGatewayV2Client<'a> {
637 fc: &'a FakeCloud,
638}
639
640impl ApiGatewayV2Client<'_> {
641 pub async fn get_requests(&self) -> Result<ApiGatewayV2RequestsResponse, Error> {
643 let resp = self
644 .fc
645 .client
646 .get(format!(
647 "{}/_fakecloud/apigatewayv2/requests",
648 self.fc.base_url
649 ))
650 .send()
651 .await?;
652 FakeCloud::parse(resp).await
653 }
654}
655
656pub struct StepFunctionsClient<'a> {
659 fc: &'a FakeCloud,
660}
661
662impl StepFunctionsClient<'_> {
663 pub async fn get_executions(&self) -> Result<StepFunctionsExecutionsResponse, Error> {
665 let resp = self
666 .fc
667 .client
668 .get(format!(
669 "{}/_fakecloud/stepfunctions/executions",
670 self.fc.base_url
671 ))
672 .send()
673 .await?;
674 FakeCloud::parse(resp).await
675 }
676}
677
678pub struct BedrockClient<'a> {
681 fc: &'a FakeCloud,
682}
683
684impl BedrockClient<'_> {
685 pub async fn get_invocations(&self) -> Result<BedrockInvocationsResponse, Error> {
688 let resp = self
689 .fc
690 .client
691 .get(format!(
692 "{}/_fakecloud/bedrock/invocations",
693 self.fc.base_url
694 ))
695 .send()
696 .await?;
697 FakeCloud::parse(resp).await
698 }
699
700 pub async fn set_model_response(
702 &self,
703 model_id: &str,
704 response: &str,
705 ) -> Result<BedrockModelResponseConfig, Error> {
706 let resp = self
707 .fc
708 .client
709 .post(format!(
710 "{}/_fakecloud/bedrock/models/{}/response",
711 self.fc.base_url, model_id
712 ))
713 .header("content-type", "text/plain")
714 .body(response.to_string())
715 .send()
716 .await?;
717 FakeCloud::parse(resp).await
718 }
719
720 pub async fn set_response_rules(
722 &self,
723 model_id: &str,
724 rules: &[BedrockResponseRule],
725 ) -> Result<BedrockModelResponseConfig, Error> {
726 let body = serde_json::json!({ "rules": rules });
727 let resp = self
728 .fc
729 .client
730 .post(format!(
731 "{}/_fakecloud/bedrock/models/{}/responses",
732 self.fc.base_url, model_id
733 ))
734 .json(&body)
735 .send()
736 .await?;
737 FakeCloud::parse(resp).await
738 }
739
740 pub async fn clear_response_rules(
742 &self,
743 model_id: &str,
744 ) -> Result<BedrockModelResponseConfig, Error> {
745 let resp = self
746 .fc
747 .client
748 .delete(format!(
749 "{}/_fakecloud/bedrock/models/{}/responses",
750 self.fc.base_url, model_id
751 ))
752 .send()
753 .await?;
754 FakeCloud::parse(resp).await
755 }
756
757 pub async fn queue_fault(
759 &self,
760 rule: &BedrockFaultRule,
761 ) -> Result<BedrockStatusResponse, Error> {
762 let resp = self
763 .fc
764 .client
765 .post(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
766 .json(rule)
767 .send()
768 .await?;
769 FakeCloud::parse(resp).await
770 }
771
772 pub async fn get_faults(&self) -> Result<BedrockFaultsResponse, Error> {
774 let resp = self
775 .fc
776 .client
777 .get(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
778 .send()
779 .await?;
780 FakeCloud::parse(resp).await
781 }
782
783 pub async fn clear_faults(&self) -> Result<BedrockStatusResponse, Error> {
785 let resp = self
786 .fc
787 .client
788 .delete(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
789 .send()
790 .await?;
791 FakeCloud::parse(resp).await
792 }
793}