Skip to content

Commit 8049584

Browse files
Merge pull request #393 from code0-tech/ref/major-codebase-refactoring
ref: major codebase refactoring
2 parents f3f84fa + 9140413 commit 8049584

41 files changed

Lines changed: 2538 additions & 1926 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/authorization/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//! Bearer-token helpers shared by every gRPC client and server in Aquila:
2+
//! [`authorization::get_authorization_metadata`] to attach a token to an
3+
//! outgoing request, [`authorization::extract_token`] to read one back off
4+
//! an incoming request.
5+
16
pub mod authorization {
27
use std::str::FromStr;
38
use tonic::{
@@ -32,6 +37,9 @@ pub mod authorization {
3237
map
3338
}
3439

40+
/// Reads and validates the bearer token off an incoming request's
41+
/// `authorization` header. Generic over the request body type so it
42+
/// works for both unary and streaming gRPC requests.
3543
pub fn extract_token<T>(request: &Request<T>) -> Result<&str, Status> {
3644
let header = request.metadata().get("authorization").ok_or_else(|| {
3745
log::warn!("Missing authorization header");
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//! Human-readable rendering of [`Config`] for the startup log line, kept
2+
//! separate from the struct definitions so the config *shape* isn't buried
3+
//! under formatting code. Must stay in sync with the redactions in
4+
//! [`super::DynamicConfig`]'s `Debug` impl — anything secret here needs the
5+
//! same `[FILTERED]` treatment.
6+
7+
use std::fmt;
8+
9+
use super::Config;
10+
11+
impl fmt::Display for Config {
12+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
13+
writeln!(formatter, "Aquila configuration")?;
14+
writeln!(formatter, " Environment: {}", self.environment)?;
15+
writeln!(formatter, " Mode: {}", self.mode)?;
16+
writeln!(formatter, " Log level: {}", self.log_level)?;
17+
writeln!(formatter, " OpenTelemetry")?;
18+
writeln!(formatter, " Enabled: {}", self.opentelemetry.enabled)?;
19+
writeln!(
20+
formatter,
21+
" Service: {}",
22+
self.opentelemetry.service_name
23+
)?;
24+
writeln!(
25+
formatter,
26+
" Logs: {}",
27+
display_optional_url(&self.opentelemetry.logs_endpoint)
28+
)?;
29+
writeln!(
30+
formatter,
31+
" Metrics: {}",
32+
display_optional_url(&self.opentelemetry.metrics_endpoint)
33+
)?;
34+
writeln!(
35+
formatter,
36+
" Traces: {}",
37+
display_optional_url(&self.opentelemetry.traces_endpoint)
38+
)?;
39+
writeln!(formatter, " NATS")?;
40+
writeln!(formatter, " URL: {}", self.nats.url)?;
41+
writeln!(formatter, " Bucket: {}", self.nats.bucket)?;
42+
writeln!(formatter, " gRPC")?;
43+
writeln!(
44+
formatter,
45+
" Address: {}:{}",
46+
self.grpc.host, self.grpc.port
47+
)?;
48+
writeln!(
49+
formatter,
50+
" Health service: {}",
51+
self.grpc.health_service
52+
)?;
53+
writeln!(formatter, " Static mode")?;
54+
writeln!(formatter, " Flow path: {}", self.static_config.flow_path)?;
55+
writeln!(formatter, " Dynamic mode")?;
56+
writeln!(
57+
formatter,
58+
" Backend URL: {}",
59+
self.dynamic_config.backend_url
60+
)?;
61+
writeln!(formatter, " Backend token: [FILTERED]")?;
62+
writeln!(
63+
formatter,
64+
" Request timeout: {}s",
65+
self.dynamic_config.backend_unary_timeout_secs
66+
)?;
67+
writeln!(formatter, " Runtime status")?;
68+
writeln!(
69+
formatter,
70+
" Not responding after: {}s",
71+
self.runtime_status.not_responding_after_secs
72+
)?;
73+
writeln!(
74+
formatter,
75+
" Stopped after: {}s",
76+
self.runtime_status.stopped_after_not_responding_secs
77+
)?;
78+
write!(
79+
formatter,
80+
" Monitor interval: {}s",
81+
self.runtime_status.monitor_interval_secs
82+
)
83+
}
84+
}
85+
86+
fn display_optional_url(url: &Option<String>) -> &str {
87+
url.as_deref()
88+
.filter(|value| !value.trim().is_empty())
89+
.unwrap_or("<disabled>")
90+
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use super::Config;
95+
96+
#[test]
97+
fn display_output_is_readable_and_filters_backend_token() {
98+
let mut config = Config::default();
99+
config.dynamic_config.backend_token = "super-secret".into();
100+
101+
let output = config.to_string();
102+
103+
assert!(output.starts_with("Aquila configuration\n"));
104+
assert!(output.contains(" Environment: development"));
105+
assert!(output.contains(" Address: 127.0.0.1:8081"));
106+
assert!(output.contains(" Request timeout: 5s"));
107+
assert!(output.contains(" Backend token: [FILTERED]"));
108+
assert!(!output.contains("super-secret"));
109+
assert!(!output.contains("Config {"));
110+
}
111+
}
Lines changed: 9 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
use std::{fmt, path::Path};
1+
//! Aquila's top-level configuration: loaded from `aquila.{yaml,toml,json,...}`
2+
//! (via the `config` crate), overridable by environment variables, and
3+
//! falling back to the [`Default`] impls below when no file is present.
4+
//!
5+
//! See [`display`] for how this renders in the startup log line.
6+
7+
mod display;
8+
9+
use std::path::Path;
210

311
use code0_flow::flow_telemetry::OpenTelemetry;
412
use config::{Config as ConfigLoader, ConfigError, File};
@@ -181,87 +189,6 @@ impl Config {
181189
}
182190
}
183191

184-
impl fmt::Display for Config {
185-
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186-
writeln!(formatter, "Aquila configuration")?;
187-
writeln!(formatter, " Environment: {}", self.environment)?;
188-
writeln!(formatter, " Mode: {}", self.mode)?;
189-
writeln!(formatter, " Log level: {}", self.log_level)?;
190-
writeln!(formatter, " OpenTelemetry")?;
191-
writeln!(formatter, " Enabled: {}", self.opentelemetry.enabled)?;
192-
writeln!(
193-
formatter,
194-
" Service: {}",
195-
self.opentelemetry.service_name
196-
)?;
197-
writeln!(
198-
formatter,
199-
" Logs: {}",
200-
display_optional_url(&self.opentelemetry.logs_endpoint)
201-
)?;
202-
writeln!(
203-
formatter,
204-
" Metrics: {}",
205-
display_optional_url(&self.opentelemetry.metrics_endpoint)
206-
)?;
207-
writeln!(
208-
formatter,
209-
" Traces: {}",
210-
display_optional_url(&self.opentelemetry.traces_endpoint)
211-
)?;
212-
writeln!(formatter, " NATS")?;
213-
writeln!(formatter, " URL: {}", self.nats.url)?;
214-
writeln!(formatter, " Bucket: {}", self.nats.bucket)?;
215-
writeln!(formatter, " gRPC")?;
216-
writeln!(
217-
formatter,
218-
" Address: {}:{}",
219-
self.grpc.host, self.grpc.port
220-
)?;
221-
writeln!(
222-
formatter,
223-
" Health service: {}",
224-
self.grpc.health_service
225-
)?;
226-
writeln!(formatter, " Static mode")?;
227-
writeln!(formatter, " Flow path: {}", self.static_config.flow_path)?;
228-
writeln!(formatter, " Dynamic mode")?;
229-
writeln!(
230-
formatter,
231-
" Backend URL: {}",
232-
self.dynamic_config.backend_url
233-
)?;
234-
writeln!(formatter, " Backend token: [FILTERED]")?;
235-
writeln!(
236-
formatter,
237-
" Request timeout: {}s",
238-
self.dynamic_config.backend_unary_timeout_secs
239-
)?;
240-
writeln!(formatter, " Runtime status")?;
241-
writeln!(
242-
formatter,
243-
" Not responding after: {}s",
244-
self.runtime_status.not_responding_after_secs
245-
)?;
246-
writeln!(
247-
formatter,
248-
" Stopped after: {}s",
249-
self.runtime_status.stopped_after_not_responding_secs
250-
)?;
251-
write!(
252-
formatter,
253-
" Monitor interval: {}s",
254-
self.runtime_status.monitor_interval_secs
255-
)
256-
}
257-
}
258-
259-
fn display_optional_url(url: &Option<String>) -> &str {
260-
url.as_deref()
261-
.filter(|value| !value.trim().is_empty())
262-
.unwrap_or("<disabled>")
263-
}
264-
265192
#[cfg(test)]
266193
mod tests {
267194
use std::sync::Mutex;
@@ -304,22 +231,6 @@ mod tests {
304231
assert!(!output.contains("super-secret"));
305232
}
306233

307-
#[test]
308-
fn display_output_is_readable_and_filters_backend_token() {
309-
let mut config = Config::default();
310-
config.dynamic_config.backend_token = "super-secret".into();
311-
312-
let output = config.to_string();
313-
314-
assert!(output.starts_with("Aquila configuration\n"));
315-
assert!(output.contains(" Environment: development"));
316-
assert!(output.contains(" Address: 127.0.0.1:8081"));
317-
assert!(output.contains(" Request timeout: 5s"));
318-
assert!(output.contains(" Backend token: [FILTERED]"));
319-
assert!(!output.contains("super-secret"));
320-
assert!(!output.contains("Config {"));
321-
}
322-
323234
#[test]
324235
fn opentelemetry_endpoints_are_enabled_by_presence() {
325236
let config: OpenTelemetry = ConfigLoader::builder()

src/configuration/env.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
//! The deployment environment Aquila believes it's running in, used to gate
2+
//! environment-specific behavior (like the dev-only flow JSON export in
3+
//! [`crate::sagittarius::flow_service_client_impl`]).
4+
15
use std::fmt;
26

37
use serde::{Deserialize, Serialize};
48

9+
/// Which deployment environment Aquila is running in.
510
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
611
#[serde(rename_all = "lowercase")]
712
pub enum Environment {

src/configuration/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//! Everything Aquila loads before it can start serving: the process-wide
2+
//! [`config::Config`] (file + env), the static [`service::ServiceConfiguration`]
3+
//! allowlist, and small shared value types ([`env::Environment`], [`mode::Mode`],
4+
//! [`state::AppReadiness`]) used across both.
5+
16
pub mod config;
27
pub mod env;
38
pub mod mode;

src/configuration/mode.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//! Aquila's two run modes: [`Mode::Static`] serves a fixed local flow export
2+
//! with no Sagittarius dependency, [`Mode::Dynamic`] syncs flows and module
3+
//! state live over gRPC. See [`crate::startup::static_mode`] and
4+
//! [`crate::startup::dynamic_mode`] for what each mode actually wires up.
5+
16
use std::fmt;
27

38
use serde::{Deserialize, Serialize};

src/configuration/service/dto.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! Wire format for the service configuration file (`AQUILA_SERVICE_CONFIG_PATH`)
2+
//! and its conversion into the domain types in the parent module.
3+
//!
4+
//! The file format is intentionally a bit friendlier than the domain model:
5+
//! actions are declared with a plain `identifier` and per-project `configs`,
6+
//! then expanded here into the [`tucana::shared::ModuleConfigurations`]
7+
//! shape the rest of Aquila works with. Runtimes need no such expansion, so
8+
//! [`RuntimeServiceConfiguration`] doubles as both the wire format and the
9+
//! domain type.
10+
11+
use serde::{Deserialize, Serialize};
12+
use tucana::shared::{ModuleConfigurations, helper::value::from_json_value};
13+
14+
use super::{ActionServiceConfiguration, ServiceConfiguration};
15+
16+
#[derive(Serialize, Deserialize, Clone)]
17+
pub(super) struct SerializableModuleConfiguration {
18+
pub(super) identifier: String,
19+
pub(super) value: serde_json::Value,
20+
}
21+
22+
#[derive(Serialize, Deserialize, Clone)]
23+
pub(super) struct SerializableModuleProjectConfiguration {
24+
pub(super) project_id: i64,
25+
#[serde(default)]
26+
pub(super) configs: Vec<SerializableModuleConfiguration>,
27+
}
28+
29+
#[derive(Serialize, Deserialize, Clone)]
30+
pub(super) struct SerializableActionServiceConfiguration {
31+
pub(super) token: String,
32+
pub(super) identifier: String,
33+
#[serde(default)]
34+
pub(super) configs: Vec<SerializableModuleProjectConfiguration>,
35+
}
36+
37+
#[derive(Serialize, Deserialize, Clone, Default)]
38+
pub(super) struct SerializableServiceConfiguration {
39+
#[serde(default)]
40+
pub(super) actions: Vec<SerializableActionServiceConfiguration>,
41+
#[serde(default)]
42+
pub(super) runtimes: Vec<RuntimeServiceConfiguration>,
43+
}
44+
45+
/// A pre-provisioned runtime's token and identity. Used directly as both the
46+
/// file format and the domain type since, unlike actions, no expansion is needed.
47+
#[derive(Serialize, Deserialize, Clone)]
48+
pub struct RuntimeServiceConfiguration {
49+
pub(super) token: String,
50+
pub(super) identifier: String,
51+
#[serde(default)]
52+
pub(super) resolved_modules: Vec<String>,
53+
}
54+
55+
impl From<SerializableModuleConfiguration> for tucana::shared::ModuleConfiguration {
56+
fn from(value: SerializableModuleConfiguration) -> Self {
57+
Self {
58+
identifier: value.identifier,
59+
value: Some(from_json_value(value.value)),
60+
}
61+
}
62+
}
63+
64+
impl From<SerializableModuleProjectConfiguration> for tucana::shared::ModuleProjectConfigurations {
65+
fn from(value: SerializableModuleProjectConfiguration) -> Self {
66+
Self {
67+
project_id: value.project_id,
68+
module_configurations: value.configs.into_iter().map(Into::into).collect(),
69+
}
70+
}
71+
}
72+
73+
impl From<SerializableActionServiceConfiguration> for ActionServiceConfiguration {
74+
fn from(value: SerializableActionServiceConfiguration) -> Self {
75+
let module_identifier = value.identifier.clone();
76+
77+
Self {
78+
token: value.token,
79+
service_name: value.identifier,
80+
config: vec![ModuleConfigurations {
81+
module_identifier,
82+
module_configurations: value.configs.into_iter().map(Into::into).collect(),
83+
}],
84+
}
85+
}
86+
}
87+
88+
impl From<SerializableServiceConfiguration> for ServiceConfiguration {
89+
fn from(value: SerializableServiceConfiguration) -> Self {
90+
Self {
91+
actions: value.actions.into_iter().map(Into::into).collect(),
92+
runtimes: value.runtimes.into_iter().collect(),
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)