Skip to content

Commit

Permalink
refactor(broker): pull string_type! macro out to the types directory
Browse files Browse the repository at this point in the history
  • Loading branch information
philbooth committed Apr 15, 2019
1 parent 7610dae commit b8389fa
Show file tree
Hide file tree
Showing 13 changed files with 374 additions and 163 deletions.
1 change: 1 addition & 0 deletions packages/fxa-event-broker/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/fxa-event-broker/Cargo.toml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ rusoto_credential = ">=0.16.0"
rusoto_sqs = ">=0.37.0" rusoto_sqs = ">=0.37.0"
sentry = ">=0.15.2" sentry = ">=0.15.2"
serde = { version = ">=1.0.90", features = ["derive"] } serde = { version = ">=1.0.90", features = ["derive"] }
serde_json = ">=1.0.39"
68 changes: 9 additions & 59 deletions packages/fxa-event-broker/src/settings.rs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,19 +7,15 @@
#[cfg(test)] #[cfg(test)]
mod test; mod test;


use std::{ use std::{convert::TryFrom, env};
convert::TryFrom,
env,
fmt::{self, Display},
};


use config::{Config, ConfigError, Environment, File}; use config::{Config, ConfigError, Environment, File};
use serde::{ use serde::{Deserialize, Serialize};
de::{Error, Unexpected},
Deserialize, Deserializer, Serialize,
};


use crate::types::{env::Env, validate}; use crate::types::{
aws::{AccessKey, Region, SecretKey, SqsUrl},
env::Env,
};


/// The root settings object. /// The root settings object.
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
Expand Down Expand Up @@ -74,7 +70,7 @@ pub struct Aws {
pub keys: Option<AwsKeys>, pub keys: Option<AwsKeys>,


/// The AWS region for SES and SQS. /// The AWS region for SES and SQS.
pub region: AwsRegion, pub region: Region,


/// URLs for SQS queues. /// URLs for SQS queues.
pub sqsurls: Option<SqsUrls>, pub sqsurls: Option<SqsUrls>,
Expand All @@ -84,10 +80,10 @@ pub struct Aws {
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
pub struct AwsKeys { pub struct AwsKeys {
/// The AWS access key. /// The AWS access key.
pub access: AwsAccess, pub access: AccessKey,


/// The AWS secret key. /// The AWS secret key.
pub secret: AwsSecret, pub secret: SecretKey,
} }


/// URLs for SQS queues. /// URLs for SQS queues.
Expand All @@ -96,49 +92,3 @@ pub struct SqsUrls {
/// The incoming queue URL. /// The incoming queue URL.
pub incoming: SqsUrl, pub incoming: SqsUrl,
} }

macro_rules! settings_strings {
($(#[$docs:meta] ($type:ident, $validator:ident, $expected:expr)),+) => ($(
#[$docs]
#[derive(Clone, Debug, Default, Serialize, PartialEq)]
pub struct $type(pub String);

impl AsRef<str> for $type {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}

impl Display for $type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}

impl<'d> Deserialize<'d> for $type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
let value: String = Deserialize::deserialize(deserializer)?;
if validate::$validator(&value) {
Ok($type(value))
} else {
let expected = $expected;
Err(D::Error::invalid_value(Unexpected::Str(&value), &expected))
}
}
}
)*);
}

settings_strings! {
/// AWS access key type.
(AwsAccess, aws_access, "AWS access key"),
/// AWS region type.
(AwsRegion, aws_region, "AWS region"),
/// AWS secret key type.
(AwsSecret, aws_secret, "AWS secret key"),
/// AWS SQS queue URL type.
(SqsUrl, sqs_url, "SQS queue URL")
}
10 changes: 5 additions & 5 deletions packages/fxa-event-broker/src/settings/test.rs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ fn env_vars_take_precedence() {
Ok(settings) => { Ok(settings) => {
let aws_keys = if let Some(ref keys) = settings.aws.keys { let aws_keys = if let Some(ref keys) = settings.aws.keys {
AwsKeys { AwsKeys {
access: AwsAccess(format!("{}A", keys.access)), access: AccessKey(format!("{}A", keys.access)),
secret: AwsSecret(format!("{}s", keys.secret)), secret: SecretKey(format!("{}s", keys.secret)),
} }
} else { } else {
AwsKeys { AwsKeys {
access: AwsAccess(String::from("A")), access: AccessKey(String::from("A")),
secret: AwsSecret(String::from("s")), secret: SecretKey(String::from("s")),
} }
}; };
let aws_region = if settings.aws.region.0 == "us-east-1" { let aws_region = if settings.aws.region.0 == "us-east-1" {
Expand Down Expand Up @@ -66,7 +66,7 @@ fn env_vars_take_precedence() {


match Settings::new() { match Settings::new() {
Ok(env_settings) => { Ok(env_settings) => {
assert_eq!(env_settings.aws.region, AwsRegion(aws_region.to_string())); assert_eq!(env_settings.aws.region, Region(aws_region.to_string()));


if let Some(env_keys) = env_settings.aws.keys { if let Some(env_keys) = env_settings.aws.keys {
assert_eq!(env_keys.access, aws_keys.access); assert_eq!(env_keys.access, aws_keys.access);
Expand Down
67 changes: 3 additions & 64 deletions packages/fxa-event-broker/src/types.rs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,71 +6,10 @@
//! for modules that implement //! for modules that implement
//! miscellaneous generally-used types. //! miscellaneous generally-used types.


macro_rules! enum_boilerplate { #[macro_use]
($name:ident ($description:expr, $default:ident, $error:ident) { pub mod macros;
$($variant:ident => $serialization:expr,)+
}) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum $name {
$($variant,
)+
}

impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
match *self {
$($name::$variant => $serialization,
)+
}
}
}

impl std::default::Default for $name {
fn default() -> Self {
$name::$default
}
}

impl std::fmt::Display for $name {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{}", self.as_ref())
}
}

impl<'d> serde::de::Deserialize<'d> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'d>,
{
let value: String = serde::de::Deserialize::deserialize(deserializer)?;
std::convert::TryFrom::try_from(value.as_str())
.map_err(|_| D::Error::invalid_value(serde::de::Unexpected::Str(&value), &$description))
}
}

impl serde::ser::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}

impl<'v> std::convert::TryFrom<&'v str> for $name {
type Error = AppError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
$($serialization => Ok($name::$variant),
)+
_ => Err(AppErrorKind::$error(value.to_owned()))?,
}
}
}
}
}


pub mod aws;
pub mod env; pub mod env;
pub mod error; pub mod error;
pub mod validate; pub mod validate;
24 changes: 24 additions & 0 deletions packages/fxa-event-broker/src/types/aws.rs
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,24 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.

//! AWS-related types.

#[cfg(test)]
mod test;

use serde::{de::Error, Serialize};

string_types! {
/// AWS region.
Region (aws_region, "AWS region", InvalidAwsRegion),

/// AWS access key.
AccessKey (aws_access_key, "AWS access key", InvalidAwsAccessKey),

/// AWS secret key.
SecretKey (aws_secret_key, "AWS secret key", InvalidAwsSecretKey),

/// SQS URL.
SqsUrl (sqs_url, "SQS queue URL", InvalidSqsUrl),
}
129 changes: 129 additions & 0 deletions packages/fxa-event-broker/src/types/aws/test.rs
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,129 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.

use std::convert::TryFrom;

use serde_json::{self, Error as JsonError};

use super::*;
use crate::types::error::AppResult;

#[test]
fn region_try_from() -> AppResult<()> {
let region: Region = TryFrom::try_from("us-east-1")?;
assert_eq!(region.as_ref(), "us-east-1");

let result: AppResult<Region> = TryFrom::try_from("us-east-7");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS region: us-east-7"
);

Ok(())
}

#[test]
fn region_serialize_deserialize() -> Result<(), JsonError> {
let region = Region("eu-west-1".to_owned());
let serialized = serde_json::to_string(&region)?;
assert_eq!(serialized, "\"eu-west-1\"");

let region: Region = serde_json::from_str(&serialized)?;
assert_eq!(region.as_ref(), "eu-west-1");

Ok(())
}

#[test]
fn access_key_try_from() -> AppResult<()> {
let access_key: AccessKey = TryFrom::try_from("0123456789ABCDEF")?;
assert_eq!(access_key.as_ref(), "0123456789ABCDEF");

let result: AppResult<AccessKey> = TryFrom::try_from("0123456789 ABCDEF");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS access key: 0123456789 ABCDEF"
);

Ok(())
}

#[test]
fn access_key_serialize_deserialize() -> Result<(), JsonError> {
let access_key = AccessKey("ABC".to_owned());
let serialized = serde_json::to_string(&access_key)?;
assert_eq!(serialized, "\"ABC\"");

let access_key: AccessKey = serde_json::from_str(&serialized)?;
assert_eq!(access_key.as_ref(), "ABC");

Ok(())
}

#[test]
fn secret_key_try_from() -> AppResult<()> {
let secret_key: SecretKey = TryFrom::try_from("0123456789ABCabc+/=")?;
assert_eq!(secret_key.as_ref(), "0123456789ABCabc+/=");

let result: AppResult<SecretKey> = TryFrom::try_from("0123456789_ABC");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS secret key: 0123456789_ABC"
);

Ok(())
}

#[test]
fn secret_key_serialize_deserialize() -> Result<(), JsonError> {
let secret_key = SecretKey("+/=".to_owned());
let serialized = serde_json::to_string(&secret_key)?;
assert_eq!(serialized, "\"+/=\"");

let secret_key: SecretKey = serde_json::from_str(&serialized)?;
assert_eq!(secret_key.as_ref(), "+/=");

Ok(())
}

#[test]
fn sqs_url_try_from() -> AppResult<()> {
let sqs_url: SqsUrl =
TryFrom::try_from("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue")?;
assert_eq!(
sqs_url.as_ref(),
"https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue"
);

let result: AppResult<SqsUrl> =
TryFrom::try_from("http://sqs.us-east-1.amazonaws.com/123456789012/MyQueue");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid SQS URL: http://sqs.us-east-1.amazonaws.com/123456789012/MyQueue"
);

Ok(())
}

#[test]
fn sqs_url_serialize_deserialize() -> Result<(), JsonError> {
let sqs_url = SqsUrl("https://sqs.us-west-2.amazonaws.com/42/wibble".to_owned());
let serialized = serde_json::to_string(&sqs_url)?;
assert_eq!(
serialized,
"\"https://sqs.us-west-2.amazonaws.com/42/wibble\""
);

let sqs_url: SqsUrl = serde_json::from_str(&serialized)?;
assert_eq!(
sqs_url.as_ref(),
"https://sqs.us-west-2.amazonaws.com/42/wibble"
);

Ok(())
}
16 changes: 8 additions & 8 deletions packages/fxa-event-broker/src/types/env.rs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ mod test;


use serde::de::Error; use serde::de::Error;


use crate::types::error::{AppError, AppErrorKind}; enum_type! {

/// Environment enumerated type.
enum_boilerplate!(Env ("env", Dev, InvalidEnv) { Env ("environment", Dev, InvalidEnv) {
// These values are consistent with the conventions followed by other FxA repos. Dev => "dev",
Dev => "dev", Prod => "production",
Prod => "production", Test => "test",
Test => "test", }
}); }
Loading

0 comments on commit b8389fa

Please sign in to comment.