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 = "ssm";

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

#[derive(Debug)]
pub enum Error {
    Json(serde_json::Error),
    Request(request::Error),
    SSM(ErrorBody),
}

impl error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Self::Json(e) => write!(f, "json error: {}", e),
            Self::Request(e) => write!(f, "http request error: {}", e),
            Self::SSM(eb) => write!(
                f,
                "ssm error{}",
                eb.message
                    .as_ref()
                    .map(|msg| format!(": {}", msg))
                    .unwrap_or_default()
            ),
        }
    }
}

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::SSM(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_parameter(&self, input: GetParameterInput) -> Result<GetParameterOutput> {
        let mut req = ureq::post(&self.url());
        req = req.set("Content-Type", "application/x-amz-json-1.1");
        req = req.set("X-Amz-Target", "AmazonSSM.GetParameter");
        self.send(req, input)
            .and_then(|response| {
                let output = serde_json::from_reader(response.into_reader())?;
                Ok(output)
            })
            .map_err(Into::into)
    }

    pub fn get_parameters_by_path(
        &self,
        input: GetParametersByPathInput,
    ) -> Result<GetParametersByPathOutput> {
        let mut req = ureq::post(&self.url());
        req = req.set("Content-Type", "application/x-amz-json-1.1");
        req = req.set("X-Amz-Target", "AmazonSSM.GetParametersByPath");
        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(From::from)
    }

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

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetParameterInput {
    #[serde(rename = "Name")]
    pub name: String,
    #[serde(rename = "WithDecryption")]
    pub with_decryption: Option<bool>,
}

impl GetParameterInput {
    pub fn name(mut self, name: &str) -> Self {
        self.name = name.to_string();
        self
    }
    pub fn with_decryption(mut self, with_decryption: bool) -> Self {
        self.with_decryption = Some(with_decryption);
        self
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetParameterOutput {
    #[serde(rename = "Parameter")]
    pub parameter: Option<Parameter>,
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetParametersByPathInput {
    #[serde(rename = "MaxResults")]
    pub max_results: Option<u32>,
    #[serde(rename = "NextToken")]
    pub next_token: Option<String>,
    #[serde(rename = "ParameterFilters")]
    pub parameter_filters: Option<Vec<ParameterStringFilter>>,
    #[serde(rename = "Path")]
    pub path: String,
    #[serde(rename = "Recursive")]
    pub recursive: Option<bool>,
    #[serde(rename = "WithDecryption")]
    pub with_decryption: Option<bool>,
}

impl GetParametersByPathInput {
    pub fn max_results(mut self, max_results: u32) -> Self {
        self.max_results = Some(max_results);
        self
    }
    pub fn next_token(mut self, next_token: &str) -> Self {
        self.next_token = Some(next_token.to_string());
        self
    }
    pub fn parameter_filters(mut self, parameter_filters: Vec<ParameterStringFilter>) -> Self {
        self.parameter_filters = Some(parameter_filters);
        self
    }
    pub fn path(mut self, path: &str) -> Self {
        self.path = path.to_string();
        self
    }
    pub fn recursive(mut self, recursive: bool) -> Self {
        self.recursive = Some(recursive);
        self
    }
    pub fn with_decryption(mut self, with_decryption: bool) -> Self {
        self.with_decryption = Some(with_decryption);
        self
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ParameterStringFilter {
    #[serde(rename = "Key")]
    pub key: String,
    #[serde(rename = "Option")]
    pub option: Option<String>,
    #[serde(rename = "Values")]
    pub values: Option<Vec<String>>,
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GetParametersByPathOutput {
    #[serde(rename = "NextToken")]
    pub next_token: Option<String>,
    #[serde(rename = "Parameters")]
    pub parameters: Option<Vec<Parameter>>,
}

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Parameter {
    #[serde(rename = "ARN")]
    pub arn: Option<String>,
    #[serde(rename = "DataType")]
    pub data_type: Option<String>,
    #[serde(rename = "LastModifiedDate")]
    pub last_modified_date: Option<f64>,
    #[serde(rename = "Name")]
    pub name: Option<String>,
    #[serde(rename = "Selector")]
    pub selector: Option<String>,
    #[serde(rename = "SourceResult")]
    pub source_result: Option<String>,
    #[serde(rename = "Type")]
    pub r#type: Option<String>,
    #[serde(rename = "Value")]
    pub value: Option<String>,
}