use crate::error::Error;
use crate::types::*;
pub struct FakeCloud {
base_url: String,
client: reqwest::Client,
}
impl FakeCloud {
pub fn new(base_url: &str) -> Self {
Self {
base_url: base_url.trim_end_matches('/').to_string(),
client: reqwest::Client::new(),
}
}
pub async fn health(&self) -> Result<HealthResponse, Error> {
let resp = self
.client
.get(format!("{}/_fakecloud/health", self.base_url))
.send()
.await?;
Self::parse(resp).await
}
pub async fn reset(&self) -> Result<ResetResponse, Error> {
let resp = self
.client
.post(format!("{}/_reset", self.base_url))
.send()
.await?;
Self::parse(resp).await
}
pub async fn create_admin(
&self,
account_id: &str,
user_name: &str,
) -> Result<CreateAdminResponse, Error> {
let resp = self
.client
.post(format!("{}/_fakecloud/iam/create-admin", self.base_url))
.json(&CreateAdminRequest {
account_id: account_id.to_string(),
user_name: user_name.to_string(),
})
.send()
.await?;
Self::parse(resp).await
}
pub async fn reset_service(&self, service: &str) -> Result<ResetServiceResponse, Error> {
let resp = self
.client
.post(format!("{}/_fakecloud/reset/{}", self.base_url, service))
.send()
.await?;
Self::parse(resp).await
}
pub async fn reset_service_for_account(
&self,
service: &str,
account_id: &str,
) -> Result<ResetServiceResponse, Error> {
let resp = self
.client
.post(format!(
"{}/_fakecloud/reset/{}/{}",
self.base_url, service, account_id
))
.send()
.await?;
Self::parse(resp).await
}
pub fn lambda(&self) -> LambdaClient<'_> {
LambdaClient { fc: self }
}
pub fn ses(&self) -> SesClient<'_> {
SesClient { fc: self }
}
pub fn sns(&self) -> SnsClient<'_> {
SnsClient { fc: self }
}
pub fn sqs(&self) -> SqsClient<'_> {
SqsClient { fc: self }
}
pub fn events(&self) -> EventsClient<'_> {
EventsClient { fc: self }
}
pub fn s3(&self) -> S3Client<'_> {
S3Client { fc: self }
}
pub fn dynamodb(&self) -> DynamoDbClient<'_> {
DynamoDbClient { fc: self }
}
pub fn secretsmanager(&self) -> SecretsManagerClient<'_> {
SecretsManagerClient { fc: self }
}
pub fn cognito(&self) -> CognitoClient<'_> {
CognitoClient { fc: self }
}
pub fn rds(&self) -> RdsClient<'_> {
RdsClient { fc: self }
}
pub fn elasticache(&self) -> ElastiCacheClient<'_> {
ElastiCacheClient { fc: self }
}
pub fn apigatewayv2(&self) -> ApiGatewayV2Client<'_> {
ApiGatewayV2Client { fc: self }
}
pub fn stepfunctions(&self) -> StepFunctionsClient<'_> {
StepFunctionsClient { fc: self }
}
pub fn bedrock(&self) -> BedrockClient<'_> {
BedrockClient { fc: self }
}
pub fn bedrock_agent(&self) -> BedrockAgentClient<'_> {
BedrockAgentClient { fc: self }
}
pub fn bedrock_agent_runtime(&self) -> BedrockAgentRuntimeClient<'_> {
BedrockAgentRuntimeClient { fc: self }
}
pub fn ecs(&self) -> EcsClient<'_> {
EcsClient { fc: self }
}
pub fn application_autoscaling(&self) -> ApplicationAutoScalingClient<'_> {
ApplicationAutoScalingClient { fc: self }
}
pub fn athena(&self) -> AthenaClient<'_> {
AthenaClient { fc: self }
}
pub fn organizations(&self) -> OrganizationsClient<'_> {
OrganizationsClient { fc: self }
}
async fn parse<T: serde::de::DeserializeOwned>(resp: reqwest::Response) -> Result<T, Error> {
let status = resp.status().as_u16();
if !resp.status().is_success() {
let body = resp.text().await.unwrap_or_default();
return Err(Error::Api { status, body });
}
Ok(resp.json::<T>().await?)
}
}
pub struct RdsClient<'a> {
fc: &'a FakeCloud,
}
impl RdsClient<'_> {
pub async fn get_instances(&self) -> Result<RdsInstancesResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/rds/instances", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct ElastiCacheClient<'a> {
fc: &'a FakeCloud,
}
impl ElastiCacheClient<'_> {
pub async fn get_clusters(&self) -> Result<ElastiCacheClustersResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/elasticache/clusters",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_replication_groups(
&self,
) -> Result<ElastiCacheReplicationGroupsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/elasticache/replication-groups",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_serverless_caches(
&self,
) -> Result<ElastiCacheServerlessCachesResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/elasticache/serverless-caches",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_acls(&self) -> Result<ElastiCacheAclsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/elasticache/acls", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct LambdaClient<'a> {
fc: &'a FakeCloud,
}
impl LambdaClient<'_> {
pub async fn get_invocations(&self) -> Result<LambdaInvocationsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/lambda/invocations",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_warm_containers(&self) -> Result<WarmContainersResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/lambda/warm-containers",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn evict_container(
&self,
function_name: &str,
) -> Result<EvictContainerResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/lambda/{}/evict-container",
self.fc.base_url, function_name
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct SesClient<'a> {
fc: &'a FakeCloud,
}
impl SesClient<'_> {
pub async fn get_emails(&self) -> Result<SesEmailsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/ses/emails", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn simulate_inbound(
&self,
req: &InboundEmailRequest,
) -> Result<InboundEmailResponse, Error> {
let resp = self
.fc
.client
.post(format!("{}/_fakecloud/ses/inbound", self.fc.base_url))
.json(req)
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct SnsClient<'a> {
fc: &'a FakeCloud,
}
impl SnsClient<'_> {
pub async fn get_messages(&self) -> Result<SnsMessagesResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/sns/messages", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_pending_confirmations(&self) -> Result<PendingConfirmationsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/sns/pending-confirmations",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn confirm_subscription(
&self,
req: &ConfirmSubscriptionRequest,
) -> Result<ConfirmSubscriptionResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/sns/confirm-subscription",
self.fc.base_url
))
.json(req)
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct SqsClient<'a> {
fc: &'a FakeCloud,
}
impl SqsClient<'_> {
pub async fn get_messages(&self) -> Result<SqsMessagesResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/sqs/messages", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn tick_expiration(&self) -> Result<ExpirationTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/sqs/expiration-processor/tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn force_dlq(&self, queue_name: &str) -> Result<ForceDlqResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/sqs/{}/force-dlq",
self.fc.base_url, queue_name
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct ApplicationAutoScalingClient<'a> {
fc: &'a FakeCloud,
}
impl ApplicationAutoScalingClient<'_> {
pub async fn tick(&self) -> Result<AppAsTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/application-autoscaling/tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn scheduled_tick(&self) -> Result<AppAsScheduledTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/application-autoscaling/scheduled-tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct EventsClient<'a> {
fc: &'a FakeCloud,
}
impl EventsClient<'_> {
pub async fn get_history(&self) -> Result<EventHistoryResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/events/history", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn fire_rule(&self, req: &FireRuleRequest) -> Result<FireRuleResponse, Error> {
let resp = self
.fc
.client
.post(format!("{}/_fakecloud/events/fire-rule", self.fc.base_url))
.json(req)
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct S3Client<'a> {
fc: &'a FakeCloud,
}
impl S3Client<'_> {
pub async fn get_notifications(&self) -> Result<S3NotificationsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/s3/notifications", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn tick_lifecycle(&self) -> Result<LifecycleTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/s3/lifecycle-processor/tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_access_points(&self) -> Result<S3AccessPointsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/s3/access-points", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_object_lambda_responses(
&self,
) -> Result<S3ObjectLambdaResponsesResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/s3/object-lambda-responses",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct DynamoDbClient<'a> {
fc: &'a FakeCloud,
}
impl DynamoDbClient<'_> {
pub async fn tick_ttl(&self) -> Result<TtlTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/dynamodb/ttl-processor/tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct SecretsManagerClient<'a> {
fc: &'a FakeCloud,
}
impl SecretsManagerClient<'_> {
pub async fn tick_rotation(&self) -> Result<RotationTickResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/secretsmanager/rotation-scheduler/tick",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct CognitoClient<'a> {
fc: &'a FakeCloud,
}
impl CognitoClient<'_> {
pub async fn get_user_codes(
&self,
pool_id: &str,
username: &str,
) -> Result<UserConfirmationCodes, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/cognito/confirmation-codes/{}/{}",
self.fc.base_url, pool_id, username
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_confirmation_codes(&self) -> Result<ConfirmationCodesResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/cognito/confirmation-codes",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn confirm_user(
&self,
req: &ConfirmUserRequest,
) -> Result<ConfirmUserResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/cognito/confirm-user",
self.fc.base_url
))
.json(req)
.send()
.await?;
let status = resp.status().as_u16();
let body: ConfirmUserResponse = resp.json().await?;
if status >= 400 {
return Err(Error::Api {
status,
body: body.error.unwrap_or_default(),
});
}
Ok(body)
}
pub async fn get_tokens(&self) -> Result<TokensResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/cognito/tokens", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn expire_tokens(
&self,
req: &ExpireTokensRequest,
) -> Result<ExpireTokensResponse, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/cognito/expire-tokens",
self.fc.base_url
))
.json(req)
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_auth_events(&self) -> Result<AuthEventsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/cognito/auth-events",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_pre_token_gen_invocations(
&self,
) -> Result<PreTokenGenInvocationsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/cognito/pretokengen/invocations",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct ApiGatewayV2Client<'a> {
fc: &'a FakeCloud,
}
impl ApiGatewayV2Client<'_> {
pub async fn get_requests(&self) -> Result<ApiGatewayV2RequestsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/apigatewayv2/requests",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct StepFunctionsClient<'a> {
fc: &'a FakeCloud,
}
impl StepFunctionsClient<'_> {
pub async fn get_executions(&self) -> Result<StepFunctionsExecutionsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/stepfunctions/executions",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_sync_executions(&self) -> Result<StepFunctionsSyncExecutionsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/stepfunctions/sync-executions",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_execution_tree(
&self,
execution_arn: &str,
) -> Result<StepFunctionsExecutionTreeResponse, Error> {
let mut encoded = String::with_capacity(execution_arn.len());
for b in execution_arn.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
encoded.push(b as char);
}
_ => encoded.push_str(&format!("%{:02X}", b)),
}
}
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/stepfunctions/execution-tree/{}",
self.fc.base_url, encoded
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct BedrockClient<'a> {
fc: &'a FakeCloud,
}
impl BedrockClient<'_> {
pub async fn get_invocations(&self) -> Result<BedrockInvocationsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/bedrock/invocations",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn set_model_response(
&self,
model_id: &str,
response: &str,
) -> Result<BedrockModelResponseConfig, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/bedrock/models/{}/response",
self.fc.base_url, model_id
))
.header("content-type", "text/plain")
.body(response.to_string())
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn set_response_rules(
&self,
model_id: &str,
rules: &[BedrockResponseRule],
) -> Result<BedrockModelResponseConfig, Error> {
let body = serde_json::json!({ "rules": rules });
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/bedrock/models/{}/responses",
self.fc.base_url, model_id
))
.json(&body)
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn clear_response_rules(
&self,
model_id: &str,
) -> Result<BedrockModelResponseConfig, Error> {
let resp = self
.fc
.client
.delete(format!(
"{}/_fakecloud/bedrock/models/{}/responses",
self.fc.base_url, model_id
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn queue_fault(
&self,
rule: &BedrockFaultRule,
) -> Result<BedrockStatusResponse, Error> {
let resp = self
.fc
.client
.post(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
.json(rule)
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_faults(&self) -> Result<BedrockFaultsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn clear_faults(&self) -> Result<BedrockStatusResponse, Error> {
let resp = self
.fc
.client
.delete(format!("{}/_fakecloud/bedrock/faults", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct BedrockAgentClient<'a> {
fc: &'a FakeCloud,
}
impl BedrockAgentClient<'_> {
pub async fn get_agents(&self) -> Result<BedrockAgentAgentsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/bedrock-agent/agents",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct BedrockAgentRuntimeClient<'a> {
fc: &'a FakeCloud,
}
impl BedrockAgentRuntimeClient<'_> {
pub async fn get_invocations(&self) -> Result<BedrockAgentRuntimeInvocationsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/bedrock-agent-runtime/invocations",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct EcsClient<'a> {
fc: &'a FakeCloud,
}
impl EcsClient<'_> {
pub async fn get_clusters(&self) -> Result<EcsClustersResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/ecs/clusters", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_tasks(
&self,
cluster: Option<&str>,
status: Option<&str>,
) -> Result<EcsTasksResponse, Error> {
fn encode(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for b in s.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(b as char);
}
_ => out.push_str(&format!("%{:02X}", b)),
}
}
out
}
let mut url = format!("{}/_fakecloud/ecs/tasks", self.fc.base_url);
let mut sep = '?';
if let Some(c) = cluster {
url.push(sep);
url.push_str("cluster=");
url.push_str(&encode(c));
sep = '&';
}
if let Some(s) = status {
url.push(sep);
url.push_str("status=");
url.push_str(&encode(s));
}
let resp = self.fc.client.get(url).send().await?;
FakeCloud::parse(resp).await
}
pub async fn get_task_logs(&self, task_id: &str) -> Result<EcsTaskLogsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/ecs/tasks/{}/logs",
self.fc.base_url, task_id
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn force_stop_task(&self, task_id: &str) -> Result<EcsTask, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/ecs/tasks/{}/force-stop",
self.fc.base_url, task_id
))
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn mark_task_failed(
&self,
task_id: &str,
req: &EcsMarkFailedRequest,
) -> Result<EcsTask, Error> {
let resp = self
.fc
.client
.post(format!(
"{}/_fakecloud/ecs/tasks/{}/mark-failed",
self.fc.base_url, task_id
))
.json(req)
.send()
.await?;
FakeCloud::parse(resp).await
}
pub async fn get_events(&self) -> Result<EcsEventsResponse, Error> {
let resp = self
.fc
.client
.get(format!("{}/_fakecloud/ecs/events", self.fc.base_url))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct AthenaClient<'a> {
fc: &'a FakeCloud,
}
impl AthenaClient<'_> {
pub async fn get_named_queries(&self) -> Result<AthenaNamedQueriesResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/athena/named-queries",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}
pub struct OrganizationsClient<'a> {
fc: &'a FakeCloud,
}
impl OrganizationsClient<'_> {
pub async fn get_accounts(&self) -> Result<OrganizationsAccountsResponse, Error> {
let resp = self
.fc
.client
.get(format!(
"{}/_fakecloud/organizations/accounts",
self.fc.base_url
))
.send()
.await?;
FakeCloud::parse(resp).await
}
}