Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add config to specify the domain where the service is hosted #490

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,12 @@ impl AppConfig {
secret = "secret-test"

[service.http]
scheme = "http"
host = "127.0.0.1"
port = 3000

[service.grpc]
scheme = "http"
host = "127.0.0.1"
port = 3001

Expand Down Expand Up @@ -224,7 +226,13 @@ impl AppConfig {
};

#[cfg(feature = "grpc")]
let config = config.add_source(service::grpc::default_config());
let config = {
let config = config.add_source(service::grpc::default_config());
let config = service::grpc::default_config_per_env(environment.clone())
.into_iter()
.fold(config, |config, source| config.add_source(source));
config
};

#[cfg(feature = "sidekiq")]
let config = config.add_source(service::worker::sidekiq::default_config());
Expand Down
49 changes: 44 additions & 5 deletions src/config/service/common/address.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::error::RoadsterResult;
use anyhow::anyhow;
use serde_derive::{Deserialize, Serialize};
use std::net::SocketAddr;
use validator::Validate;
Expand All @@ -8,6 +7,7 @@ use validator::Validate;
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Address {
pub scheme: String,
pub host: String,
pub port: u32,
}
Expand All @@ -16,11 +16,50 @@ impl Address {
pub fn url(&self) -> String {
format!("{}:{}", self.host, self.port)
}

pub fn url_with_scheme(&self) -> String {
format!("{}://{}:{}", self.scheme, self.host, self.port)
}

pub fn socket_addr(&self) -> RoadsterResult<SocketAddr> {
let addr = self
.url()
.parse()
.map_err(|e| anyhow!("Unable to parse app url to a SocketAddr: {e}"))?;
let addr = self.url().parse()?;
Ok(addr)
}
}

#[cfg(test)]
mod tests {
use crate::config::service::common::address::Address;
use crate::testing::snapshot::TestCase;
use insta::assert_debug_snapshot;
use rstest::{fixture, rstest};
use url::Url;

#[fixture]
fn case() -> TestCase {
Default::default()
}

#[rstest]
#[case(
r#"
scheme = "http"
host = "localhost"
port = 1234
"#
)]
#[case(
r#"
scheme = "https"
host = "[::]"
port = 3000
"#
)]
fn url_with_scheme(_case: TestCase, #[case] address: &str) {
let addr: Address = toml::from_str(address).unwrap();

let url: Result<Url, _> = addr.url_with_scheme().parse();

assert_debug_snapshot!(url);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: src/config/service/common/address.rs
expression: url
---
Ok(
Url {
scheme: "http",
cannot_be_a_base: false,
username: "",
password: None,
host: Some(
Domain(
"localhost",
),
),
port: Some(
1234,
),
path: "/",
query: None,
fragment: None,
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: src/config/service/common/address.rs
expression: url
---
Ok(
Url {
scheme: "https",
cannot_be_a_base: false,
username: "",
password: None,
host: Some(
Ipv6(
::,
),
),
port: Some(
3000,
),
path: "/",
query: None,
fragment: None,
},
)
2 changes: 2 additions & 0 deletions src/config/service/grpc/config/default.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[service.grpc]
scheme = "http"
2 changes: 2 additions & 0 deletions src/config/service/grpc/config/production.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[service.grpc]
scheme = "https"
Empty file.
13 changes: 12 additions & 1 deletion src/config/service/grpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
use crate::config::environment::Environment;
use crate::config::service::common::address::Address;
use config::{FileFormat, FileSourceString};
use serde_derive::{Deserialize, Serialize};
use validator::Validate;

pub fn default_config() -> config::File<FileSourceString, FileFormat> {
config::File::from_str(include_str!("default.toml"), FileFormat::Toml)
config::File::from_str(include_str!("config/default.toml"), FileFormat::Toml)
}

pub(crate) fn default_config_per_env(
environment: Environment,
) -> Option<config::File<FileSourceString, FileFormat>> {
let config = match environment {
Environment::Production => Some(include_str!("config/production.toml")),
_ => None,
};
config.map(|c| config::File::from_str(c, FileFormat::Toml))
}

#[derive(Debug, Clone, Validate, Serialize, Deserialize)]
Expand Down
3 changes: 3 additions & 0 deletions src/config/service/http/config/default.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[service.http]
scheme = "http"

# Middleware
[service.http.middleware]
default-enable = true
Expand Down
3 changes: 3 additions & 0 deletions src/config/service/http/config/production.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[service.http]
scheme = "https"

[service.http.middleware.request-response-logging]
enable = false
max-len = 0
16 changes: 16 additions & 0 deletions src/config/service/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ use crate::config::environment::Environment;
use crate::config::service::common::address::Address;
use crate::config::service::http::initializer::Initializer;
use crate::config::service::http::middleware::Middleware;
use crate::error::RoadsterResult;
use config::{FileFormat, FileSourceString};
use default_routes::DefaultRoutes;
use serde_derive::{Deserialize, Serialize};
use url::Url;
use validator::Validate;

pub mod default_routes;
Expand All @@ -29,6 +31,8 @@ pub(crate) fn default_config_per_env(
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct HttpServiceConfig {
/// A URL for the domain where the service is hosted, if available.
pub url: Option<Url>,
#[serde(flatten)]
#[validate(nested)]
pub address: Address,
Expand All @@ -39,3 +43,15 @@ pub struct HttpServiceConfig {
#[validate(nested)]
pub default_routes: DefaultRoutes,
}

impl HttpServiceConfig {
/// Get either the URL for the domain where the service is hosted if available, or the URL for
/// the socket address where the service is running locally.
pub fn url(&self) -> RoadsterResult<Url> {
if let Some(url) = self.url.as_ref() {
Ok(url.clone())
} else {
Ok(self.address.url_with_scheme().parse()?)
}
}
}
2 changes: 2 additions & 0 deletions src/config/snapshots/roadster__config__tests__test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ cli = 10000
default-enable = true

[service.http]
scheme = 'http'
host = '127.0.0.1'
port = 3000

Expand Down Expand Up @@ -122,6 +123,7 @@ route = '_docs'
route = '_docs/redoc'

[service.grpc]
scheme = 'http'
host = '127.0.0.1'
port = 3001

Expand Down
5 changes: 5 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod email;
#[cfg(feature = "http")]
pub mod mime;
pub mod other;
pub mod parse;
pub mod reqwest;
pub mod serde;
#[cfg(feature = "sidekiq")]
Expand All @@ -26,6 +27,7 @@ use crate::error::email::EmailError;
#[cfg(feature = "http")]
use crate::error::mime::MimeError;
use crate::error::other::OtherError;
use crate::error::parse::ParseError;
use crate::error::reqwest::ReqwestError;
use crate::error::serde::SerdeError;
#[cfg(feature = "sidekiq")]
Expand Down Expand Up @@ -90,6 +92,9 @@ pub enum Error {
#[error(transparent)]
Axum(#[from] AxumError),

#[error(transparent)]
Parse(#[from] ParseError),

#[cfg(feature = "http")]
#[error(transparent)]
Mime(#[from] MimeError),
Expand Down
27 changes: 27 additions & 0 deletions src/error/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::error::Error;
use std::net::AddrParseError;

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ParseError {
#[error(transparent)]
Url(#[from] url::ParseError),

#[error(transparent)]
Address(#[from] AddrParseError),

#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}

impl From<url::ParseError> for Error {
fn from(value: url::ParseError) -> Self {
Self::Parse(ParseError::from(value))
}
}

impl From<AddrParseError> for Error {
fn from(value: AddrParseError) -> Self {
Self::Parse(ParseError::from(value))
}
}