minaws 0.3.0

A synchronous subset of the AWS SDK.
Documentation
use std::{error, fmt::Display};

use aws_credential_types::Credentials;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use ureq::Response;

use crate::request::{self, sign_request, with_retry};

const SERVICE_NAME: &str = "secretsmanager";

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    Api(u16, Box<Response>),
    Json(serde_json::Error),
    Request(request::Error),
    SecretsManager(ErrorBody),
    Transport(Box<ureq::Error>),
}

impl error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Error::Json(err)
    }
}

impl From<request::Error> for Error {
    fn from(err: request::Error) -> Self {
        match err {
            request::Error::Api(_, response) => {
                let body = response.into_reader();
                match serde_json::from_reader(body) {
                    Ok(err_body) => Error::SecretsManager(err_body),
                    Err(e) => Error::Json(e),
                }
            }
            request::Error::SigningError(signing_error) => {
                Error::Request(request::Error::SigningError(signing_error))
            }
            request::Error::Transport(transport_error) => {
                Error::Request(request::Error::Transport(transport_error))
            }
        }
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ErrorBody {
    #[serde(rename = "__type")]
    pub r#type: String,
    #[serde(rename = "Message")]
    pub message: Option<String>,
}

impl Display for ErrorBody {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

#[derive(Clone, Debug)]
pub struct Api {
    credentials: Credentials,
    region: String,
}

impl Api {
    pub fn new(region: &str, credentials: Credentials) -> Self {
        Self {
            region: region.to_string(),
            credentials,
        }
    }

    pub fn get_secret_value(&self, input: GetSecretValueInput) -> Result<GetSecretValueOutput> {
        let mut req = ureq::post(&self.url());
        req = req.set("Content-Type", "application/x-amz-json-1.1");
        req = req.set("X-Amz-Target", "secretsmanager.GetSecretValue");
        self.send(req, input)
            .and_then(|response| {
                let output = serde_json::from_reader(response.into_reader())?;
                Ok(output)
            })
            .map_err(Into::into)
    }

    fn send<I: Serialize>(&self, mut req: ureq::Request, input: I) -> Result<Response> {
        let body = serde_json::to_vec(&input)?;
        let identity = self.credentials.clone().into();
        req = sign_request(req, &body, &identity, &self.region, SERVICE_NAME)?;
        with_retry(|| req.clone().send_bytes(&body), 5).map_err(Into::into)
    }

    fn url(&self) -> String {
        format!("https://{}.{}.amazonaws.com", SERVICE_NAME, self.region)
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetSecretValueInput {
    #[serde(rename = "SecretId")]
    pub secret_id: String,
    #[serde(rename = "VersionId")]
    pub version_id: Option<String>,
    #[serde(rename = "VersionStage")]
    pub version_stage: Option<String>,
}

impl GetSecretValueInput {
    pub fn secret_id(mut self, secret_id: &str) -> Self {
        self.secret_id = secret_id.into();
        self
    }
    pub fn version_id(mut self, version_id: &str) -> Self {
        self.version_id = Some(version_id.into());
        self
    }
    pub fn version_stage(mut self, version_stage: &str) -> Self {
        self.version_stage = Some(version_stage.into());
        self
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetSecretValueOutput {
    #[serde(rename = "ARN")]
    pub arn: String,
    #[serde(rename = "CreatedDate")]
    pub created_date: f64,
    #[serde(rename = "Name")]
    pub name: String,
    #[serde(rename = "SecretBinary")]
    pub secret_binary: Option<Vec<u8>>,
    #[serde(rename = "SecretString")]
    pub secret_string: Option<String>,
    #[serde(rename = "VersionId")]
    pub version_id: String,
    #[serde(rename = "VersionStages")]
    pub version_stages: Vec<String>,
}