0.5.1-pre.007
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
// file: ks-config/src/composition.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Binary composition configuration and resolution into the shared runtime profile contract.
|
||||
|
||||
const COMPOSITION_JSON_SCHEMA: &str =
|
||||
include_str!("../../config/schemas/composition.config.schema.json");
|
||||
const DEFAULT_LOGGING_CONFIG_PATH: &str = "config/logging.config.json";
|
||||
const DEFAULT_TRANSPORT_CONFIG_PATH: &str = "config/transport.config.json";
|
||||
const DEFAULT_LISTENERS_CONFIG_PATH: &str = "config/listeners.config.json";
|
||||
const DEFAULT_STORE_CONFIG_PATH: &str = "config/store.config.json";
|
||||
const DEFAULT_WALLET_CONFIG_PATH: &str = "config/wallet.config.json";
|
||||
const DEFAULT_EXECUTION_CONFIG_PATH: &str = "config/execution.config.json";
|
||||
|
||||
/// Root composition document used by a binary to select shared configuration profiles.
|
||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||
@@ -26,33 +32,58 @@ pub struct CompositionConfigSources {
|
||||
pub transport: std::string::String,
|
||||
/// Listener configuration document path relative to the workspace root unless absolute.
|
||||
pub listeners: std::string::String,
|
||||
/// Store configuration document path relative to the workspace root unless absolute.
|
||||
pub store: std::string::String,
|
||||
/// Wallet configuration document path relative to the workspace root unless absolute.
|
||||
pub wallet: std::string::String,
|
||||
/// Execution configuration document path relative to the workspace root unless absolute.
|
||||
pub execution: std::string::String,
|
||||
}
|
||||
|
||||
/// Named composition profile referencing shared configuration profiles.
|
||||
/// Named composition profile with optional overrides of each shared document default.
|
||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||
pub struct CompositionProfileConfig {
|
||||
/// Composition profile code.
|
||||
pub name: std::string::String,
|
||||
/// Application metadata retained until the remaining configuration split is completed.
|
||||
/// Application metadata retained until the application-specific split is completed.
|
||||
pub app: crate::AppSectionConfig,
|
||||
/// Logging profile selected from the referenced logging document.
|
||||
pub logging_profile: std::string::String,
|
||||
/// Transport profile selected from the referenced transport document.
|
||||
pub transport_profile: std::string::String,
|
||||
/// Listener profile selected from the referenced listener document.
|
||||
pub listeners_profile: std::string::String,
|
||||
/// Database configuration retained until the store split prerelease.
|
||||
pub database: crate::DatabaseConfig,
|
||||
/// Local data directories retained until their owning configuration files are split.
|
||||
pub data: crate::DataConfig,
|
||||
/// Wallet configuration retained until the wallet split prerelease.
|
||||
pub wallet: crate::WalletConfig,
|
||||
/// Execution policy retained until the execution split prerelease.
|
||||
pub execution: crate::ExecutionConfig,
|
||||
/// Desktop demo flags retained until the binary-specific split prerelease.
|
||||
/// Optional logging profile override.
|
||||
pub logging_profile: std::option::Option<std::string::String>,
|
||||
/// Optional transport profile override.
|
||||
pub transport_profile: std::option::Option<std::string::String>,
|
||||
/// Optional listener profile override.
|
||||
pub listeners_profile: std::option::Option<std::string::String>,
|
||||
/// Optional store profile override.
|
||||
pub store_profile: std::option::Option<std::string::String>,
|
||||
/// Optional wallet profile override.
|
||||
pub wallet_profile: std::option::Option<std::string::String>,
|
||||
/// Optional execution profile override.
|
||||
pub execution_profile: std::option::Option<std::string::String>,
|
||||
/// Desktop demo flags retained until the application-specific split is completed.
|
||||
pub demo: crate::DemoConfig,
|
||||
}
|
||||
|
||||
/// Resolved application configuration and the source documents needed by a binary.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ComposedAppConfig {
|
||||
/// Parsed binary composition document.
|
||||
pub composition: CompositionConfigDocument,
|
||||
/// Resolved runtime application configuration.
|
||||
pub app_config: crate::AppConfig,
|
||||
/// Resolved logging source path.
|
||||
pub logging_path: std::path::PathBuf,
|
||||
/// Resolved transport source path.
|
||||
pub transport_path: std::path::PathBuf,
|
||||
/// Resolved listener source path.
|
||||
pub listeners_path: std::path::PathBuf,
|
||||
/// Resolved store source path.
|
||||
pub store_path: std::path::PathBuf,
|
||||
/// Resolved wallet source path.
|
||||
pub wallet_path: std::path::PathBuf,
|
||||
/// Resolved execution source path.
|
||||
pub execution_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
/// Returns the embedded binary composition JSON Schema text.
|
||||
pub fn composition_json_schema_text() -> &'static str {
|
||||
return COMPOSITION_JSON_SCHEMA;
|
||||
@@ -60,9 +91,9 @@ pub fn composition_json_schema_text() -> &'static str {
|
||||
|
||||
/// Parses the embedded binary composition JSON Schema into a JSON value.
|
||||
pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
let schema_result = serde_json::from_str::<serde_json::Value>(COMPOSITION_JSON_SCHEMA);
|
||||
return match schema_result {
|
||||
std::result::Result::Ok(schema) => std::result::Result::Ok(schema),
|
||||
let result = serde_json::from_str::<serde_json::Value>(COMPOSITION_JSON_SCHEMA);
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_schema_parse_failed",
|
||||
error.to_string(),
|
||||
@@ -73,11 +104,11 @@ pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
/// Validates raw binary composition JSON against its embedded schema.
|
||||
pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match composition_json_schema_value() {
|
||||
std::result::Result::Ok(schema) => schema,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let instance = match serde_json::from_str::<serde_json::Value>(raw_json) {
|
||||
std::result::Result::Ok(instance) => instance,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_json_parse_failed",
|
||||
@@ -86,7 +117,7 @@ pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
},
|
||||
};
|
||||
let validator = match jsonschema::validator_for(&schema) {
|
||||
std::result::Result::Ok(validator) => validator,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_schema_compile_failed",
|
||||
@@ -110,7 +141,7 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConf
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(document) => document,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_json_decode_failed",
|
||||
@@ -129,7 +160,7 @@ pub fn read_composition_json_file(
|
||||
path: &std::path::Path,
|
||||
) -> ks_core::Result<CompositionConfigDocument> {
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(content) => content,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_file_read_failed",
|
||||
@@ -150,7 +181,7 @@ pub fn read_composition_json_file_with_environment(
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(content) => content,
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_config_file_read_failed",
|
||||
@@ -158,8 +189,7 @@ pub fn read_composition_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
let resolved = crate::resolve_environment_placeholders(&raw_json);
|
||||
return parse_composition_json(&resolved);
|
||||
return parse_composition_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Returns one named binary composition profile.
|
||||
@@ -197,36 +227,79 @@ pub fn resolve_composition_source_path(
|
||||
return workspace_root.join(path);
|
||||
}
|
||||
|
||||
/// Resolves all shared transport/listener selections into the existing runtime application contract.
|
||||
/// Resolves one optional composition override against a shared document default profile.
|
||||
pub fn resolve_profile_selection<'a>(
|
||||
override_name: std::option::Option<&'a str>,
|
||||
default_profile: &'a str,
|
||||
) -> &'a str {
|
||||
return match override_name {
|
||||
std::option::Option::Some(value) if !value.trim().is_empty() => value,
|
||||
_ => default_profile,
|
||||
};
|
||||
}
|
||||
|
||||
/// Resolves all shared source selections into the runtime application contract.
|
||||
pub fn compose_app_config(
|
||||
document: &CompositionConfigDocument,
|
||||
transport: &crate::TransportConfigDocument,
|
||||
listeners: &crate::ListenersConfigDocument,
|
||||
store: &crate::StoreConfigDocument,
|
||||
wallet: &crate::WalletConfigDocument,
|
||||
execution: &crate::ExecutionConfigDocument,
|
||||
) -> ks_core::Result<crate::AppConfig> {
|
||||
let mut profiles = std::vec::Vec::<crate::ProfileConfig>::new();
|
||||
for profile in &document.profiles {
|
||||
let transport_profile =
|
||||
match crate::transport_profile(transport, profile.transport_profile.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let listeners_profile =
|
||||
match crate::listeners_profile(listeners, profile.listeners_profile.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transport_name = resolve_profile_selection(
|
||||
profile.transport_profile.as_deref(),
|
||||
transport.default_profile.as_str(),
|
||||
);
|
||||
let listeners_name = resolve_profile_selection(
|
||||
profile.listeners_profile.as_deref(),
|
||||
listeners.default_profile.as_str(),
|
||||
);
|
||||
let store_name = resolve_profile_selection(
|
||||
profile.store_profile.as_deref(),
|
||||
store.default_profile.as_str(),
|
||||
);
|
||||
let wallet_name = resolve_profile_selection(
|
||||
profile.wallet_profile.as_deref(),
|
||||
wallet.default_profile.as_str(),
|
||||
);
|
||||
let execution_name = resolve_profile_selection(
|
||||
profile.execution_profile.as_deref(),
|
||||
execution.default_profile.as_str(),
|
||||
);
|
||||
let transport_profile = match crate::transport_profile(transport, transport_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let listeners_profile = match crate::listeners_profile(listeners, listeners_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store_profile = match crate::store_profile(store, store_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let wallet_profile = match crate::resolved_wallet_profile(wallet, wallet_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let execution_profile = match crate::execution_profile(execution, execution_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
profiles.push(crate::ProfileConfig {
|
||||
name: profile.name.clone(),
|
||||
app: profile.app.clone(),
|
||||
database: profile.database.clone(),
|
||||
data: profile.data.clone(),
|
||||
database: store_profile.database.clone(),
|
||||
solana: crate::SolanaConfig {
|
||||
http_endpoints: transport_profile.http_endpoints.clone(),
|
||||
ws_endpoints: transport_profile.ws_endpoints.clone(),
|
||||
http_endpoints: transport_profile.http_endpoints,
|
||||
ws_endpoints: transport_profile.ws_endpoints,
|
||||
listeners: crate::resolved_listener_config(listeners_profile),
|
||||
},
|
||||
wallet: profile.wallet.clone(),
|
||||
execution: profile.execution.clone(),
|
||||
wallet: wallet_profile,
|
||||
execution: execution_profile.config.clone(),
|
||||
demo: profile.demo.clone(),
|
||||
});
|
||||
}
|
||||
@@ -240,20 +313,28 @@ pub fn compose_app_config(
|
||||
};
|
||||
}
|
||||
|
||||
/// Reads a binary composition and its referenced transport/listener documents into the resolved runtime contract.
|
||||
/// Reads a binary composition and all referenced shared documents into the runtime contract.
|
||||
pub fn read_composed_app_config_with_environment(
|
||||
composition_path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<crate::ComposedAppConfig> {
|
||||
) -> ks_core::Result<ComposedAppConfig> {
|
||||
let composition =
|
||||
match read_composition_json_file_with_environment(composition_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let logging_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.logging.as_str());
|
||||
let transport_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.transport.as_str());
|
||||
let listeners_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.listeners.as_str());
|
||||
let store_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.store.as_str());
|
||||
let wallet_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.wallet.as_str());
|
||||
let execution_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.execution.as_str());
|
||||
let transport =
|
||||
match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -264,34 +345,120 @@ pub fn read_composed_app_config_with_environment(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let app_config = match compose_app_config(&composition, &transport, &listeners) {
|
||||
let store = match crate::read_store_json_file_with_environment(&store_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::ComposedAppConfig {
|
||||
let wallet = match crate::read_wallet_json_file_with_environment(&wallet_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let execution =
|
||||
match crate::read_execution_json_file_with_environment(&execution_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match validate_logging_references_with_environment(&logging_path, workspace_root, &composition)
|
||||
{
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let app_config =
|
||||
match compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution)
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(ComposedAppConfig {
|
||||
composition,
|
||||
app_config,
|
||||
logging_path,
|
||||
transport_path,
|
||||
listeners_path,
|
||||
store_path,
|
||||
wallet_path,
|
||||
execution_path,
|
||||
});
|
||||
}
|
||||
|
||||
/// Resolved application configuration and the source documents needed by a binary.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ComposedAppConfig {
|
||||
/// Parsed binary composition document.
|
||||
pub composition: CompositionConfigDocument,
|
||||
/// Resolved runtime application configuration.
|
||||
pub app_config: crate::AppConfig,
|
||||
/// Resolved transport source path.
|
||||
pub transport_path: std::path::PathBuf,
|
||||
/// Resolved listener source path.
|
||||
pub listeners_path: std::path::PathBuf,
|
||||
/// Loads canonical shared documents and composes their independent default profiles without an application composition file.
|
||||
pub fn read_default_shared_app_config_with_environment(
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<crate::AppConfig> {
|
||||
let transport_path = workspace_root.join(DEFAULT_TRANSPORT_CONFIG_PATH);
|
||||
let listeners_path = workspace_root.join(DEFAULT_LISTENERS_CONFIG_PATH);
|
||||
let store_path = workspace_root.join(DEFAULT_STORE_CONFIG_PATH);
|
||||
let wallet_path = workspace_root.join(DEFAULT_WALLET_CONFIG_PATH);
|
||||
let execution_path = workspace_root.join(DEFAULT_EXECUTION_CONFIG_PATH);
|
||||
let transport =
|
||||
match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let listeners =
|
||||
match crate::read_listeners_json_file_with_environment(&listeners_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store = match crate::read_store_json_file_with_environment(&store_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let wallet = match crate::read_wallet_json_file_with_environment(&wallet_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let execution =
|
||||
match crate::read_execution_json_file_with_environment(&execution_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let defaults = [
|
||||
transport.default_profile.as_str(),
|
||||
listeners.default_profile.as_str(),
|
||||
store.default_profile.as_str(),
|
||||
wallet.default_profile.as_str(),
|
||||
execution.default_profile.as_str(),
|
||||
];
|
||||
let profile_name = if defaults.iter().all(|value| return *value == defaults[0]) {
|
||||
defaults[0].to_string()
|
||||
} else {
|
||||
"shared_default".to_string()
|
||||
};
|
||||
let composition = CompositionConfigDocument {
|
||||
active_profile: profile_name.clone(),
|
||||
sources: CompositionConfigSources {
|
||||
logging: DEFAULT_LOGGING_CONFIG_PATH.to_string(),
|
||||
transport: DEFAULT_TRANSPORT_CONFIG_PATH.to_string(),
|
||||
listeners: DEFAULT_LISTENERS_CONFIG_PATH.to_string(),
|
||||
store: DEFAULT_STORE_CONFIG_PATH.to_string(),
|
||||
wallet: DEFAULT_WALLET_CONFIG_PATH.to_string(),
|
||||
execution: DEFAULT_EXECUTION_CONFIG_PATH.to_string(),
|
||||
},
|
||||
profiles: vec![CompositionProfileConfig {
|
||||
name: profile_name.clone(),
|
||||
app: crate::AppSectionConfig {
|
||||
name: "khadhroony-solana".to_string(),
|
||||
environment: profile_name,
|
||||
},
|
||||
logging_profile: std::option::Option::None,
|
||||
transport_profile: std::option::Option::None,
|
||||
listeners_profile: std::option::Option::None,
|
||||
store_profile: std::option::Option::None,
|
||||
wallet_profile: std::option::Option::None,
|
||||
execution_profile: std::option::Option::None,
|
||||
demo: crate::DemoConfig {
|
||||
live_demo_enabled: false,
|
||||
trading_demo_enabled: false,
|
||||
},
|
||||
}],
|
||||
};
|
||||
return compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution);
|
||||
}
|
||||
|
||||
/// Validates a typed binary composition document before referenced documents are loaded.
|
||||
pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.active_profile, "active_profile") {
|
||||
match crate::require_non_empty(&document.active_profile, "composition.active_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -299,6 +466,9 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
||||
&document.sources.logging,
|
||||
&document.sources.transport,
|
||||
&document.sources.listeners,
|
||||
&document.sources.store,
|
||||
&document.sources.wallet,
|
||||
&document.sources.execution,
|
||||
] {
|
||||
match crate::require_non_empty(source, "composition.sources") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
@@ -317,12 +487,19 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
for reference in
|
||||
[&profile.logging_profile, &profile.transport_profile, &profile.listeners_profile]
|
||||
{
|
||||
match crate::require_non_empty(reference, "composition.profile.reference") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
for reference in [
|
||||
profile.logging_profile.as_ref(),
|
||||
profile.transport_profile.as_ref(),
|
||||
profile.listeners_profile.as_ref(),
|
||||
profile.store_profile.as_ref(),
|
||||
profile.wallet_profile.as_ref(),
|
||||
profile.execution_profile.as_ref(),
|
||||
] {
|
||||
if let std::option::Option::Some(value) = reference
|
||||
&& let std::result::Result::Err(error) =
|
||||
crate::require_non_empty(value, "composition.profile.reference")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
}
|
||||
if !names.insert(profile.name.clone()) {
|
||||
@@ -338,14 +515,85 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
||||
};
|
||||
}
|
||||
|
||||
fn validate_logging_references_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
composition: &CompositionConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_file_read_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
let resolved = crate::resolve_environment_placeholders(&raw_json);
|
||||
let value = match serde_json::from_str::<serde_json::Value>(&resolved) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_json_parse_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
let default_profile = match value.get("default_profile").and_then(serde_json::Value::as_str) {
|
||||
std::option::Option::Some(value) if !value.trim().is_empty() => value,
|
||||
_ => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_default_profile_missing",
|
||||
path.display().to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
let profiles = match value.get("profiles").and_then(serde_json::Value::as_array) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_profiles_missing",
|
||||
path.display().to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
let mut names = std::collections::BTreeSet::<std::string::String>::new();
|
||||
for profile in profiles {
|
||||
if let std::option::Option::Some(name) =
|
||||
profile.get("name").and_then(serde_json::Value::as_str)
|
||||
{
|
||||
names.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
if !names.contains(default_profile) {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_default_profile_not_found",
|
||||
default_profile.to_string(),
|
||||
));
|
||||
}
|
||||
for profile in &composition.profiles {
|
||||
let selected =
|
||||
resolve_profile_selection(profile.logging_profile.as_deref(), default_profile);
|
||||
if !names.contains(selected) {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_profile_not_found",
|
||||
selected.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
const DEFAULT_COMPOSITION: &str =
|
||||
include_str!("../../config/kb-app-demo-desktop.default.config.json");
|
||||
const EXAMPLE_COMPOSITION: &str =
|
||||
include_str!("../../config/example.kb-app-demo-desktop.default.config.json");
|
||||
const DEFAULT_TRANSPORT: &str = include_str!("../../config/transport.config.json");
|
||||
const DEFAULT_LISTENERS: &str = include_str!("../../config/listeners.config.json");
|
||||
|
||||
#[test]
|
||||
fn default_and_example_compositions_validate() {
|
||||
@@ -354,20 +602,19 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_composition_resolves_shared_transport_and_listener_profiles() {
|
||||
fn local_devnet_composition_uses_shared_defaults() {
|
||||
let composition = match super::parse_composition_json(DEFAULT_COMPOSITION) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("default composition must parse: {error}"),
|
||||
};
|
||||
let transport = match crate::parse_transport_json(DEFAULT_TRANSPORT) {
|
||||
let profile = match super::composition_profile(&composition, "local_devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("default transport must parse: {error}"),
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("local Devnet composition must resolve: {error}")
|
||||
},
|
||||
};
|
||||
let listeners = match crate::parse_listeners_json(DEFAULT_LISTENERS) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("default listeners must parse: {error}"),
|
||||
};
|
||||
let resolved = super::compose_app_config(&composition, &transport, &listeners);
|
||||
assert!(resolved.is_ok());
|
||||
assert!(profile.logging_profile.is_none());
|
||||
assert!(profile.transport_profile.is_none());
|
||||
assert!(profile.store_profile.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user