diff --git a/eppo_core/src/configuration.rs b/eppo_core/src/configuration.rs index f8a6c3c3..72eef8bf 100644 --- a/eppo_core/src/configuration.rs +++ b/eppo_core/src/configuration.rs @@ -12,7 +12,8 @@ pub struct Configuration { pub flags: Option, /// Bandits configuration. pub bandits: Option, - /// Mapping from flag key to flag variation value to bandit variation. + /// Mapping from flag key to flag variation value to bandit variation. Cached from + /// `self.flags.bandits`. pub flag_to_bandit_associations: HashMap>, } diff --git a/eppo_core/src/configuration_store.rs b/eppo_core/src/configuration_store.rs index 2e105437..d32f5f71 100644 --- a/eppo_core/src/configuration_store.rs +++ b/eppo_core/src/configuration_store.rs @@ -46,8 +46,13 @@ impl ConfigurationStore { mod tests { use std::{collections::HashMap, sync::Arc}; + use chrono::Utc; + use super::ConfigurationStore; - use crate::{ufc::UniversalFlagConfig, Configuration}; + use crate::{ + ufc::{Environment, UniversalFlagConfig}, + Configuration, + }; #[test] fn can_set_configuration_from_another_thread() { @@ -58,6 +63,10 @@ mod tests { let _ = std::thread::spawn(move || { store.set_configuration(Configuration::new( Some(UniversalFlagConfig { + created_at: Utc::now(), + environment: Environment { + name: "test".to_owned(), + }, flags: HashMap::new(), bandits: HashMap::new(), }), diff --git a/eppo_core/src/ufc/models.rs b/eppo_core/src/ufc/models.rs index 7280dedd..0fea4260 100644 --- a/eppo_core/src/ufc/models.rs +++ b/eppo_core/src/ufc/models.rs @@ -12,6 +12,10 @@ pub type Timestamp = chrono::DateTime; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct UniversalFlagConfig { + /// When configuration was last updated. + pub created_at: Timestamp, + /// Environment this configuration belongs to. + pub environment: Environment, /// Flags configuration. /// /// Value is wrapped in `TryParse` so that if we fail to parse one flag (e.g., new server @@ -23,6 +27,13 @@ pub struct UniversalFlagConfig { pub bandits: HashMap>, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Environment { + /// Name of the environment. + pub name: String, +} + /// `TryParse` allows the subfield to fail parsing without failing the parsing of the whole /// structure. /// @@ -314,6 +325,8 @@ mod tests { let ufc: UniversalFlagConfig = serde_json::from_str( &r#" { + "createdAt": "2024-07-18T00:00:00Z", + "environment": {"name": "test"}, "flags": { "success": { "key": "success", diff --git a/rust-sdk/Cargo.toml b/rust-sdk/Cargo.toml index 6acfbc18..a21536ff 100644 --- a/rust-sdk/Cargo.toml +++ b/rust-sdk/Cargo.toml @@ -19,4 +19,5 @@ serde_json = "1.0.116" name = "simple" [dev-dependencies] +chrono = "0.4.38" env_logger = { version = "0.11.3", features = ["unstable-kv"] } diff --git a/rust-sdk/src/client.rs b/rust-sdk/src/client.rs index b322b0fe..142c37be 100644 --- a/rust-sdk/src/client.rs +++ b/rust-sdk/src/client.rs @@ -362,7 +362,10 @@ mod tests { use crate::{client::AssignmentValue, Client, ClientConfig}; use eppo_core::{ configuration_store::ConfigurationStore, - ufc::{Allocation, Flag, Split, TryParse, UniversalFlagConfig, Variation, VariationType}, + ufc::{ + Allocation, Environment, Flag, Split, TryParse, UniversalFlagConfig, Variation, + VariationType, + }, Configuration, }; @@ -393,6 +396,10 @@ mod tests { // updating configuration after client is created configuration_store.set_configuration(Configuration::new( Some(UniversalFlagConfig { + created_at: chrono::Utc::now(), + environment: Environment { + name: "test".to_owned(), + }, flags: [( "flag".to_owned(), TryParse::Parsed(Flag {