diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1b6817e96..eb3652b17 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -218,8 +218,7 @@ jobs: echo "Docker image: ${{ env.DOCKER_IMAGE_NAME }}@${{ needs.docker.outputs.docker_image_digest }}" docker run -d --name router -p 4000:4000 \ - -e HIVE__SUPERGRAPH__SOURCE="file" \ - -e HIVE__SUPERGRAPH__PATH="/app/supergraph.graphql" \ + -e SUPERGRAPH_FILE_PATH="/app/supergraph.graphql" \ -v ./bench/supergraph.graphql:/app/supergraph.graphql \ ${{ env.DOCKER_IMAGE_NAME }}@${{ needs.docker.outputs.docker_image_digest }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 213258346..87678cdfb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -130,8 +130,7 @@ jobs: - name: Run router run: ./target/release/hive_router & sleep 5 env: - HIVE__SUPERGRAPH__SOURCE: file - HIVE__SUPERGRAPH__PATH: bench/supergraph.graphql + SUPERGRAPH_FILE_PATH: bench/supergraph.graphql - name: Run k6 benchmark run: k6 run bench/k6.js diff --git a/Cargo.lock b/Cargo.lock index 42f56c6a7..e0e1bc295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,6 +1505,26 @@ dependencies = [ "log", ] +[[package]] +name = "envconfig" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1d02ec9fdd0a585580bdc8fb7ad01675eee5e3b7336cedbabe3aab4a026dbc" +dependencies = [ + "envconfig_derive", +] + +[[package]] +name = "envconfig_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4291f0c7220b67ad15e9d5300ba2f215cee504f0924d60e77c9d1c77e7a69b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -2033,6 +2053,7 @@ name = "hive-router-config" version = "0.0.9" dependencies = [ "config", + "envconfig", "http", "humantime-serde", "jsonwebtoken", @@ -2041,6 +2062,7 @@ dependencies = [ "serde", "serde_json", "thiserror 2.0.17", + "tracing", ] [[package]] diff --git a/README.md b/README.md index a2e2eed18..ceec87604 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ supergraph: Alternativly, you can use environment variables to configure the router: ```env -HIVE__SUPERGRAPH__SOURCE=file -HIVE__SUPERGRAPH__PATH=./supergraph.graphql +SUPERGRAPH_FILE_PATH=./supergraph.graphql ``` Then, run the router: @@ -58,8 +57,7 @@ The router image is being published to [Docker to GitHub Container Registry](). ```bash docker run \ -p 4000:4000 \ - -e HIVE__SUPERGRAPH__SOURCE="file" \ - -e HIVE__SUPERGRAPH__PATH="/app/supergraph.graphql" \ + -e SUPERGRAPH_FILE_PATH="/app/supergraph.graphql" \ -v ./my-supergraph.graphql:/app/supergraph.graphql \ ghcr.io/graphql-hive/router:latest ``` diff --git a/audits/package.json b/audits/package.json index 3b68af1ba..365b8c4c1 100644 --- a/audits/package.json +++ b/audits/package.json @@ -5,7 +5,7 @@ "scripts": { "test:federation-all": "graphql-federation-audit test --run-script=\"./run-router.sh\" --graphql=\"http://localhost:4000/graphql\" --healthcheck=\"http://localhost:4000/health\" --junit", "test:federation-single": "graphql-federation-audit test-suite --run-script=\"./run-router.sh\" --graphql=\"http://localhost:4000/graphql\" --healthcheck=\"http://localhost:4000/health\" --write=\"federation-audit-results.txt\"", - "start:test-router": "cd .. && HIVE__SUPERGRAPH__SOURCE=file HIVE__SUPERGRAPH__PATH=lib/query-planner/fixture/spotify-supergraph.graphql cargo router", + "start:test-router": "cd .. && SUPERGRAPH_FILE_PATH=lib/query-planner/fixture/spotify-supergraph.graphql cargo router", "test:graphql-over-http": "node --test graphql-over-http.test.js", "test-junit:graphql-over-http": "node --test --test-reporter=junit --test-reporter-destination=\"./reports/graphql-over-http.xml\" graphql-over-http.test.js", "ci:test:graphql-over-http": "start-server-and-test start:test-router http://localhost:4000 test-junit:graphql-over-http" diff --git a/audits/run-router.sh b/audits/run-router.sh index 8b3f81597..8bdbcecc4 100644 --- a/audits/run-router.sh +++ b/audits/run-router.sh @@ -11,6 +11,5 @@ echo "running router..." cd .. -export HIVE__SUPERGRAPH__SOURCE="file" -export HIVE__SUPERGRAPH__PATH="audits/fed-audit-supergraph.graphql" +export SUPERGRAPH_FILE_PATH="audits/fed-audit-supergraph.graphql" cargo router diff --git a/bench/README.md b/bench/README.md index 01dfb02c5..f4d94449c 100644 --- a/bench/README.md +++ b/bench/README.md @@ -3,8 +3,7 @@ ``` cargo subgraphs -export HIVE__SUPERGRAPH__SOURCE="file" -export HIVE__SUPERGRAPH__PATH="bench/supergraph.graphql" +export SUPERGRAPH_FILE_PATH="bench/supergraph.graphql" cargo router ``` diff --git a/bin/router/src/main.rs b/bin/router/src/main.rs index 0e980014a..b4f250b38 100644 --- a/bin/router/src/main.rs +++ b/bin/router/src/main.rs @@ -5,5 +5,12 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; #[ntex::main] async fn main() -> Result<(), Box> { - router_entrypoint().await + match router_entrypoint().await { + Ok(_) => Ok(()), + Err(err) => { + eprintln!("Failed to start Hive Router:\n {}", err); + + Err(err) + } + } } diff --git a/bin/router/src/pipeline/mod.rs b/bin/router/src/pipeline/mod.rs index 122dd5472..459e85870 100644 --- a/bin/router/src/pipeline/mod.rs +++ b/bin/router/src/pipeline/mod.rs @@ -53,9 +53,13 @@ pub async fn graphql_request_handler( schema_state: &Arc, ) -> web::HttpResponse { if req.method() == Method::GET && req.accepts_content_type(*TEXT_HTML_CONTENT_TYPE) { - return web::HttpResponse::Ok() - .header(CONTENT_TYPE, *TEXT_HTML_CONTENT_TYPE) - .body(GRAPHIQL_HTML); + if shared_state.router_config.graphiql.enabled { + return web::HttpResponse::Ok() + .header(CONTENT_TYPE, *TEXT_HTML_CONTENT_TYPE) + .body(GRAPHIQL_HTML); + } else { + return web::HttpResponse::NotFound().into(); + } } if let Some(jwt) = &shared_state.jwt_auth_runtime { diff --git a/docker/README.md b/docker/README.md index 417024110..acf1b442d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -37,8 +37,7 @@ Use the following command to run the Docker image locally: ```bash docker run -p 4000:4000 \ - -e HIVE__SUPERGRAPH__SOURCE="file" \ - -e HIVE__SUPERGRAPH__PATH="/app/supergraph.graphql" \ + -e SUPERGRAPH_FILE_PATH="/app/supergraph.graphql" \ -v ./bench/supergraph.graphql:/app/supergraph.graphql \ ``` diff --git a/docs/README.md b/docs/README.md index 81557feee..3b2fd2716 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ |----|----|-----------|--------| |[**cors**](#cors)|`object`|Configuration for CORS (Cross-Origin Resource Sharing).
Default: `{"allow_any_origin":false,"allow_credentials":false,"enabled":false,"policies":[]}`
|yes| |[**csrf**](#csrf)|`object`|Configuration for CSRF prevention.
Default: `{"enabled":false,"required_headers":[]}`
|| +|[**graphiql**](#graphiql)|`object`|Configuration for the GraphiQL interface.
Default: `{"enabled":true}`
|| |[**headers**](#headers)|`object`|Configuration for the headers.
Default: `{}`
|| |[**http**](#http)|`object`|Configuration for the HTTP server/listener.
Default: `{"host":"0.0.0.0","port":4000}`
|| |[**jwt**](#jwt)|`object`, `null`|Configuration for JWT authentication plugin.
|yes| @@ -36,6 +37,8 @@ csrf: enabled: true required_headers: - x-csrf-token +graphiql: + enabled: true headers: all: request: @@ -328,6 +331,26 @@ A valid HTTP header name, according to RFC 7230. **Item Type:** `string` **Item Pattern:** `^[A-Za-z0-9!#$%&'*+\-.^_\`\|~]+$` + +## graphiql: object + +Configuration for the GraphiQL interface. + + +**Properties** + +|Name|Type|Description|Required| +|----|----|-----------|--------| +|**enabled**|`boolean`|Enables/disables the GraphiQL interface. By default, the GraphiQL interface is enabled.

You can override this setting by setting the `GRAPHIQL_ENABLED` environment variable to `true` or `false`.
Default: `true`
|| + +**Additional Properties:** not allowed +**Example** + +```yaml +enabled: true + +``` + ## headers: object @@ -1304,8 +1327,8 @@ Configuration for the HTTP server/listener. |Name|Type|Description|Required| |----|----|-----------|--------| -|**host**|`string`|The host address to bind the HTTP server to.
Default: `"0.0.0.0"`
|| -|**port**|`integer`|The port to bind the HTTP server to.

If you are running the router inside a Docker container, please ensure that the port is exposed correctly using `-p :` flag.
Default: `4000`
Format: `"uint16"`
Minimum: `0`
Maximum: `65535`
|| +|**host**|`string`|The host address to bind the HTTP server to.

Can also be set via the `HOST` environment variable.
Default: `"0.0.0.0"`
|| +|**port**|`integer`|The port to bind the HTTP server to.

Can also be set via the `PORT` environment variable.

If you are running the router inside a Docker container, please ensure that the port is exposed correctly using `-p :` flag.
Default: `4000`
Format: `"uint16"`
Minimum: `0`
Maximum: `65535`
|| **Additional Properties:** not allowed **Example** @@ -1499,9 +1522,9 @@ The router is configured to be mostly silent (`info`) level, and will print only |Name|Type|Description|Required| |----|----|-----------|--------| -|**filter**|`string`, `null`||| -|**format**|`string`|Default: `"json"`
Enum: `"pretty-tree"`, `"pretty-compact"`, `"json"`
|| -|**level**|`string`|Default: `"info"`
Enum: `"trace"`, `"debug"`, `"info"`, `"warn"`, `"error"`
|| +|**filter**|`string`, `null`|The filter to apply to log messages.

Can also be set via the `LOG_FILTER` environment variable.
|| +|**format**|`string`|The format of the log messages.

Can also be set via the `LOG_FORMAT` environment variable.
Default: `"json"`
Enum: `"pretty-tree"`, `"pretty-compact"`, `"json"`
|| +|**level**|`string`|The level of logging to use.

Can also be set via the `LOG_LEVEL` environment variable.
Default: `"info"`
Enum: `"trace"`, `"debug"`, `"info"`, `"warn"`, `"error"`
|| **Additional Properties:** not allowed **Example** @@ -1593,7 +1616,7 @@ The path can be either absolute or relative to the router's working directory. |Name|Type|Description|Required| |----|----|-----------|--------| -|**path**|`string`|Format: `"path"`
|yes| +|**path**|`string`|The path to the supergraph file.

Can also be set using the `SUPERGRAPH_FILE_PATH` environment variable.
Format: `"path"`
|yes| |[**poll\_interval**](#option1poll_interval)|`object`, `null`|Optional interval at which the file should be polled for changes.
|yes| |**source**|`string`|Constant Value: `"file"`
|yes| @@ -1615,8 +1638,8 @@ Loads a supergraph from Hive Console CDN. |Name|Type|Description|Required| |----|----|-----------|--------| -|**endpoint**|`string`|The CDN endpoint from Hive Console target.
|yes| -|**key**|`string`|The CDN Access Token with from the Hive Console target.
|yes| +|**endpoint**|`string`|The CDN endpoint from Hive Console target.

Can also be set using the `HIVE_CDN_ENDPOINT` environment variable.
|yes| +|**key**|`string`|The CDN Access Token with from the Hive Console target.

Can also be set using the `HIVE_CDN_KEY` environment variable.
|yes| |[**poll\_interval**](#option2poll_interval)|`object`|Interval at which the Hive Console should be polled for changes.
Default: `"10s"`
|yes| |[**retry\_policy**](#option2retry_policy)|`object`|Interval at which the Hive Console should be polled for changes.
Default: `{"max_retries":10}`
|yes| |**source**|`string`|Constant Value: `"hive"`
|yes| @@ -1660,6 +1683,8 @@ If not provided, the file will only be loaded once when the router starts. Interval at which the Hive Console should be polled for changes. +Can also be set using the `HIVE_CDN_POLL_INTERVAL` environment variable. + **Properties** diff --git a/lib/router-config/Cargo.toml b/lib/router-config/Cargo.toml index 45dc21cc8..7c93c4891 100644 --- a/lib/router-config/Cargo.toml +++ b/lib/router-config/Cargo.toml @@ -22,7 +22,9 @@ thiserror = { workspace = true } http = { workspace = true } jsonwebtoken = { workspace = true } retry-policies = { workspace = true} +tracing = { workspace = true } schemars = "1.0.4" humantime-serde = "1.1.1" config = { version = "0.15.14", features = ["yaml", "json", "json5"] } +envconfig = "0.11.0" diff --git a/lib/router-config/src/env_overrides.rs b/lib/router-config/src/env_overrides.rs new file mode 100644 index 000000000..f61012967 --- /dev/null +++ b/lib/router-config/src/env_overrides.rs @@ -0,0 +1,109 @@ +use config::{builder::BuilderState, ConfigBuilder, ConfigError}; +use envconfig::Envconfig; +use tracing::debug; + +use crate::log::{LogFormat, LogLevel}; + +#[derive(Envconfig)] +pub struct EnvVarOverrides { + // Logger overrides + #[envconfig(from = "LOG_LEVEL")] + pub log_level: Option, + #[envconfig(from = "LOG_FORMAT")] + pub log_format: Option, + #[envconfig(from = "LOG_FILTER")] + pub log_filter: Option, + + // GraphiQL overrides + #[envconfig(from = "GRAPHIQL_ENABLED")] + pub graphiql_enabled: Option, + + // HTTP overrides + #[envconfig(from = "PORT")] + pub http_port: Option, + #[envconfig(from = "HOST")] + pub http_host: Option, + + // Supergraph overrides + #[envconfig(from = "SUPERGRAPH_FILE_PATH")] + pub supergraph_file_path: Option, + #[envconfig(from = "HIVE_CDN_ENDPOINT")] + pub hive_console_cdn_endpoint: Option, + #[envconfig(from = "HIVE_CDN_KEY")] + pub hive_console_cdn_key: Option, + #[envconfig(from = "HIVE_CDN_POLL_INTERVAL")] + pub hive_console_cdn_poll_interval: Option, +} + +#[derive(Debug, thiserror::Error)] +pub enum EnvVarOverridesError { + #[error("Failed to override configuration: {0}")] + FailedToOverrideConfig(#[from] ConfigError), + #[error("Cannot override supergraph source due to conflict: SUPERGRAPH_FILE_PATH and HIVE_CDN_ENDPOINT cannot be used together")] + ConflictingSupergraphSource, + #[error("Missing required environment variable: {0}")] + MissingRequiredEnvVar(&'static str), +} + +impl EnvVarOverrides { + pub fn apply_overrides( + mut self, + mut config: ConfigBuilder, + ) -> Result, EnvVarOverridesError> { + if let Some(log_level) = self.log_level.take() { + debug!("[config-override] 'log.level' = {:?}", log_level); + config = config.set_override("log.level", log_level.as_str())?; + } + if let Some(log_format) = self.log_format.take() { + debug!("[config-override] 'log.format' = {:?}", log_format); + config = config.set_override("log.format", log_format.as_str())?; + } + if let Some(log_filter) = self.log_filter.take() { + debug!("[config-override] 'log.filter' = {:?}", log_filter); + config = config.set_override("log.filter", log_filter)?; + } + + if let Some(http_port) = self.http_port.take() { + debug!("[config-override] 'http.port' = {}", http_port); + config = config.set_override("http.port", http_port)?; + } + + if let Some(http_host) = self.http_host.take() { + debug!("[config-override] 'http.host' = {}", http_host); + config = config.set_override("http.host", http_host)?; + } + + if self.supergraph_file_path.is_some() && self.hive_console_cdn_endpoint.is_some() { + return Err(EnvVarOverridesError::ConflictingSupergraphSource); + } + + if let Some(supergraph_file_path) = self.supergraph_file_path.take() { + config = config.set_override("supergraph.source", "file")?; + config = config.set_override("supergraph.path", supergraph_file_path)?; + } + + if let Some(hive_console_cdn_endpoint) = self.hive_console_cdn_endpoint.take() { + config = config.set_override("supergraph.source", "hive")?; + config = config.set_override("supergraph.endpoint", hive_console_cdn_endpoint)?; + + if let Some(hive_console_cdn_key) = self.hive_console_cdn_key.take() { + config = config.set_override("supergraph.key", hive_console_cdn_key)?; + } else { + return Err(EnvVarOverridesError::MissingRequiredEnvVar("HIVE_CDN_KEY")); + } + + if let Some(hive_console_cdn_poll_interval) = self.hive_console_cdn_poll_interval.take() + { + config = config + .set_override("supergraph.poll_interval", hive_console_cdn_poll_interval)?; + } + } + + // GraphiQL overrides + if let Some(graphiql_enabled) = self.graphiql_enabled.take() { + config = config.set_override("graphiql.enabled", graphiql_enabled)?; + } + + Ok(config) + } +} diff --git a/lib/router-config/src/graphiql.rs b/lib/router-config/src/graphiql.rs new file mode 100644 index 000000000..6d453c26d --- /dev/null +++ b/lib/router-config/src/graphiql.rs @@ -0,0 +1,24 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)] +#[serde(deny_unknown_fields)] +pub struct GraphiQLConfig { + /// Enables/disables the GraphiQL interface. By default, the GraphiQL interface is enabled. + /// + /// You can override this setting by setting the `GRAPHIQL_ENABLED` environment variable to `true` or `false`. + #[serde(default = "default_graphiql_enabled")] + pub enabled: bool, +} + +fn default_graphiql_enabled() -> bool { + true +} + +impl Default for GraphiQLConfig { + fn default() -> Self { + Self { + enabled: default_graphiql_enabled(), + } + } +} diff --git a/lib/router-config/src/http_server.rs b/lib/router-config/src/http_server.rs index c03696826..c4bf9048e 100644 --- a/lib/router-config/src/http_server.rs +++ b/lib/router-config/src/http_server.rs @@ -5,11 +5,15 @@ use serde::{Deserialize, Serialize}; #[serde(deny_unknown_fields)] pub struct HttpServerConfig { /// The host address to bind the HTTP server to. + /// + /// Can also be set via the `HOST` environment variable. #[serde(default = "http_server_host_default")] host: String, /// The port to bind the HTTP server to. /// + /// Can also be set via the `PORT` environment variable. + /// /// If you are running the router inside a Docker container, please ensure that the port is exposed correctly using `-p :` flag. #[serde(default = "http_server_port_default")] port: u16, diff --git a/lib/router-config/src/lib.rs b/lib/router-config/src/lib.rs index b7ad64d55..5f8b78fde 100644 --- a/lib/router-config/src/lib.rs +++ b/lib/router-config/src/lib.rs @@ -1,5 +1,7 @@ pub mod cors; pub mod csrf; +mod env_overrides; +pub mod graphiql; pub mod headers; pub mod http_server; pub mod jwt_auth; @@ -10,14 +12,20 @@ pub mod query_planner; pub mod supergraph; pub mod traffic_shaping; -use config::{Config, Environment, File, FileFormat, FileSourceFile}; +use config::{Config, File, FileFormat, FileSourceFile}; +use envconfig::Envconfig; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use crate::{ - http_server::HttpServerConfig, log::LoggingConfig, primitives::file_path::with_start_path, - query_planner::QueryPlannerConfig, supergraph::SupergraphSource, + env_overrides::{EnvVarOverrides, EnvVarOverridesError}, + graphiql::GraphiQLConfig, + http_server::HttpServerConfig, + log::LoggingConfig, + primitives::file_path::with_start_path, + query_planner::QueryPlannerConfig, + supergraph::SupergraphSource, traffic_shaping::TrafficShapingExecutorConfig, }; @@ -33,6 +41,10 @@ pub struct HiveRouterConfig { #[serde(default)] pub log: LoggingConfig, + /// Configuration for the GraphiQL interface. + #[serde(default)] + pub graphiql: GraphiQLConfig, + /// Configuration for the Federation supergraph source. By default, the router will use a local file-based supergraph source (`./supergraph.graphql`). /// Each source has a different set of configuration, depending on the source type. #[serde(default)] @@ -74,7 +86,9 @@ pub struct HiveRouterConfig { #[derive(Debug, thiserror::Error)] pub enum RouterConfigError { #[error("Failed to load configuration: {0}")] - ConfigLoadError(config::ConfigError), + ConfigLoadError(#[from] config::ConfigError), + #[error("Failed to apply configuration overrides: {0}")] + EnvVarOverridesError(#[from] EnvVarOverridesError), } static DEFAULT_FILE_NAMES: &[&str] = &[ @@ -86,7 +100,8 @@ static DEFAULT_FILE_NAMES: &[&str] = &[ pub fn load_config( overide_config_path: Option, -) -> Result { +) -> Result { + let env_overrides = EnvVarOverrides::init_from_env().expect("failed to init env overrides"); let mut config = Config::builder(); let mut config_root_path = std::env::current_dir().expect("failed to get current directory"); @@ -106,15 +121,10 @@ pub fn load_config( } } + config = env_overrides.apply_overrides(config)?; + let mut base_cfg = with_start_path(&config_root_path, || { - config - .add_source( - Environment::with_prefix("HIVE") - .separator("__") - .prefix_separator("__"), - ) - .build()? - .try_deserialize::() + config.build()?.try_deserialize::() })?; base_cfg.root_directory = config_root_path; diff --git a/lib/router-config/src/log.rs b/lib/router-config/src/log.rs index 0d617c36d..074aaa1ad 100644 --- a/lib/router-config/src/log.rs +++ b/lib/router-config/src/log.rs @@ -1,13 +1,26 @@ +use std::str::FromStr; + use schemars::JsonSchema; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct LoggingConfig { + /// The level of logging to use. + /// + /// Can also be set via the `LOG_LEVEL` environment variable. #[serde(default)] pub level: LogLevel, + + /// The format of the log messages. + /// + /// Can also be set via the `LOG_FORMAT` environment variable. #[serde(default)] pub format: LogFormat, + + /// The filter to apply to log messages. + /// + /// Can also be set via the `LOG_FILTER` environment variable. #[serde(default)] pub filter: Option, } @@ -28,6 +41,21 @@ pub enum LogLevel { Error, } +impl FromStr for LogLevel { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "trace" => Ok(LogLevel::Trace), + "debug" => Ok(LogLevel::Debug), + "info" => Ok(LogLevel::Info), + "warn" => Ok(LogLevel::Warn), + "error" => Ok(LogLevel::Error), + _ => Err(format!("Invalid log level: {}", s)), + } + } +} + impl Default for LogLevel { #[cfg(debug_assertions)] fn default() -> Self { @@ -62,6 +90,29 @@ pub enum LogFormat { Json, } +impl LogFormat { + pub fn as_str(&self) -> &'static str { + match self { + LogFormat::PrettyTree => "pretty-tree", + LogFormat::PrettyCompact => "pretty-compact", + LogFormat::Json => "json", + } + } +} + +impl FromStr for LogFormat { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "pretty-tree" => Ok(LogFormat::PrettyTree), + "pretty-compact" => Ok(LogFormat::PrettyCompact), + "json" => Ok(LogFormat::Json), + _ => Err(format!("Invalid log format: {}", s)), + } + } +} + impl Default for LogFormat { #[cfg(debug_assertions)] fn default() -> Self { diff --git a/lib/router-config/src/supergraph.rs b/lib/router-config/src/supergraph.rs index 44659e599..60b2c41f0 100644 --- a/lib/router-config/src/supergraph.rs +++ b/lib/router-config/src/supergraph.rs @@ -12,6 +12,9 @@ pub enum SupergraphSource { /// The path can be either absolute or relative to the router's working directory. #[serde(rename = "file")] File { + /// The path to the supergraph file. + /// + /// Can also be set using the `SUPERGRAPH_FILE_PATH` environment variable. path: FilePath, /// Optional interval at which the file should be polled for changes. /// If not provided, the file will only be loaded once when the router starts. @@ -26,10 +29,16 @@ pub enum SupergraphSource { #[serde(rename = "hive")] HiveConsole { /// The CDN endpoint from Hive Console target. + /// + /// Can also be set using the `HIVE_CDN_ENDPOINT` environment variable. endpoint: String, /// The CDN Access Token with from the Hive Console target. + /// + /// Can also be set using the `HIVE_CDN_KEY` environment variable. key: String, /// Interval at which the Hive Console should be polled for changes. + /// + /// Can also be set using the `HIVE_CDN_POLL_INTERVAL` environment variable. #[serde( default = "default_hive_poll_interval", deserialize_with = "humantime_serde::deserialize",