-
Notifications
You must be signed in to change notification settings - Fork 161
feat(data): introduce DataApi trait and enhance DataStore functionality #13059
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
Changes from all commits
a6bbb10
47315f1
653c1b7
9634d72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // This software is available under the Apache-2.0 license. | ||
| // See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. | ||
| // | ||
| // Copyright (c) 2024, Gluu, Inc. | ||
|
|
||
| //! Data API trait and supporting types for the DataStore. | ||
|
|
||
| use std::time::Duration; | ||
|
|
||
| use serde::{Deserialize, Serialize}; | ||
| use serde_json::Value; | ||
|
|
||
| use super::entry::DataEntry; | ||
| use super::error::DataError; | ||
|
|
||
| /// Statistics about the DataStore. | ||
| /// | ||
| /// Provides insight into the current state and usage of the data store. | ||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct DataStoreStats { | ||
| /// Number of entries currently stored | ||
| pub entry_count: usize, | ||
| /// Maximum number of entries allowed (0 = unlimited) | ||
| pub max_entries: usize, | ||
| /// Maximum size per entry in bytes (0 = unlimited) | ||
| pub max_entry_size: usize, | ||
| /// Whether metrics tracking is enabled | ||
| pub metrics_enabled: bool, | ||
| } | ||
|
|
||
| /// Trait defining the public API for data store operations. | ||
| /// | ||
| /// This trait provides a consistent interface for pushing, retrieving, | ||
| /// and managing data in the store. All operations are thread-safe. | ||
| /// | ||
| /// The [`Cedarling`](crate::Cedarling) struct implements this trait, providing | ||
| /// access to the data store through the main application instance. | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// ```no_run | ||
| /// use cedarling::{Cedarling, DataApi, DataError}; | ||
| /// use serde_json::json; | ||
| /// use std::time::Duration; | ||
| /// | ||
| /// fn use_data_api(cedarling: &Cedarling) -> Result<(), DataError> { | ||
| /// // Push data with a 5-minute TTL | ||
| /// cedarling.push_data( | ||
| /// "user_roles", | ||
| /// json!(["admin", "editor"]), | ||
| /// Some(Duration::from_secs(300)), | ||
| /// )?; | ||
| /// | ||
| /// // Retrieve data | ||
| /// if let Some(roles) = cedarling.get_data("user_roles")? { | ||
| /// println!("User roles: {}", roles); | ||
| /// } | ||
| /// | ||
| /// // List all entries with metadata | ||
| /// for entry in cedarling.list_data()? { | ||
| /// println!("Key: {}, Type: {:?}", entry.key, entry.data_type); | ||
| /// } | ||
| /// | ||
| /// // Get store statistics | ||
| /// let stats = cedarling.get_stats()?; | ||
| /// println!("Entries: {}/{}", stats.entry_count, stats.max_entries); | ||
| /// | ||
| /// // Remove data | ||
| /// cedarling.remove_data("user_roles")?; | ||
| /// | ||
| /// // Clear all data | ||
| /// cedarling.clear_data()?; | ||
| /// | ||
| /// Ok(()) | ||
| /// } | ||
| /// ``` | ||
| pub trait DataApi { | ||
| /// Push a value into the store with an optional TTL. | ||
| /// | ||
| /// If the key already exists, the value will be replaced. | ||
| /// If TTL is not provided, the default TTL from configuration is used. | ||
| fn push_data(&self, key: &str, value: Value, ttl: Option<Duration>) -> Result<(), DataError>; | ||
|
|
||
| /// Get a value from the store by key. | ||
| /// | ||
| /// Returns `Ok(None)` if the key doesn't exist or the entry has expired. | ||
| /// If metrics are enabled, increments the access count for the entry. | ||
| fn get_data(&self, key: &str) -> Result<Option<Value>, DataError>; | ||
|
|
||
| /// Get a data entry with full metadata by key. | ||
| /// | ||
| /// Returns `Ok(None)` if the key doesn't exist or the entry has expired. | ||
| /// Includes metadata like creation time, expiration, access count, and type. | ||
| fn get_data_entry(&self, key: &str) -> Result<Option<DataEntry>, DataError>; | ||
|
|
||
| /// Remove a value from the store by key. | ||
| /// | ||
| /// Returns `Ok(true)` if the key existed and was removed, `Ok(false)` otherwise. | ||
| fn remove_data(&self, key: &str) -> Result<bool, DataError>; | ||
|
|
||
| /// Clear all entries from the store. | ||
| fn clear_data(&self) -> Result<(), DataError>; | ||
|
|
||
| /// List all entries with their metadata. | ||
| /// | ||
| /// Returns a vector of `DataEntry` containing key, value, type, and timing metadata. | ||
| fn list_data(&self) -> Result<Vec<DataEntry>, DataError>; | ||
|
|
||
| /// Get statistics about the data store. | ||
| /// | ||
| /// Returns current entry count, capacity limits, and configuration state. | ||
| fn get_stats(&self) -> Result<DataStoreStats, DataError>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,11 @@ mod tests; | |
| use std::sync::Arc; | ||
|
|
||
| pub use crate::common::json_rules::JsonRule; | ||
| pub use crate::data::{CedarType, ConfigValidationError, DataEntry, DataStoreConfig}; | ||
| use crate::data::DataStore; | ||
| pub use crate::data::{ | ||
| CedarType, ConfigValidationError, DataApi, DataEntry, DataError, DataStoreConfig, | ||
| DataStoreStats, | ||
| }; | ||
| pub use crate::init::policy_store::{PolicyStoreLoadError, load_policy_store}; | ||
| use crate::log::BaseLogEntry; | ||
| #[cfg(test)] | ||
|
|
@@ -99,6 +103,7 @@ pub enum InitCedarlingError { | |
| pub struct Cedarling { | ||
| log: log::Logger, | ||
| authz: Arc<Authz>, | ||
| data: Arc<DataStore>, | ||
| } | ||
|
|
||
| impl Cedarling { | ||
|
|
@@ -156,9 +161,17 @@ impl Cedarling { | |
| log_policy_store_metadata(&log, metadata); | ||
| } | ||
|
|
||
| // Initialize data store with default configuration | ||
| // TODO: Add DataStoreConfig to BootstrapConfig | ||
| let data = Arc::new( | ||
| DataStore::new(DataStoreConfig::default()) | ||
| .expect("default DataStoreConfig should always be valid"), | ||
| ); | ||
|
|
||
| Ok(Cedarling { | ||
| log, | ||
| authz: service_factory.authz_service().await?, | ||
| data, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -331,3 +344,47 @@ impl LogStorage for Cedarling { | |
| self.log.get_logs_by_request_id_and_tag(id, tag) | ||
| } | ||
| } | ||
|
|
||
| // implements DataApi for Cedarling | ||
| // provides public interface for pushing and retrieving data | ||
| impl DataApi for Cedarling { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no u didn't miss anything ur right, am currently refactoring it because i also have to update the tests thats why i will push the fix in a moment but thanks for pointing it out
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved in 9634d72 |
||
| fn push_data( | ||
| &self, | ||
| key: &str, | ||
| value: serde_json::Value, | ||
| ttl: Option<std::time::Duration>, | ||
| ) -> Result<(), DataError> { | ||
| self.data.push(key, value, ttl) | ||
| } | ||
|
|
||
| fn get_data(&self, key: &str) -> Result<Option<serde_json::Value>, DataError> { | ||
| Ok(self.data.get(key)) | ||
| } | ||
|
|
||
| fn get_data_entry(&self, key: &str) -> Result<Option<DataEntry>, DataError> { | ||
| Ok(self.data.get_entry(key)) | ||
| } | ||
|
|
||
| fn remove_data(&self, key: &str) -> Result<bool, DataError> { | ||
| Ok(self.data.remove(key)) | ||
| } | ||
|
|
||
| fn clear_data(&self) -> Result<(), DataError> { | ||
| self.data.clear(); | ||
| Ok(()) | ||
| } | ||
|
|
||
| fn list_data(&self) -> Result<Vec<DataEntry>, DataError> { | ||
| Ok(self.data.list_entries()) | ||
| } | ||
|
|
||
| fn get_stats(&self) -> Result<DataStoreStats, DataError> { | ||
| let config = self.data.config(); | ||
| Ok(DataStoreStats { | ||
| entry_count: self.data.count(), | ||
| max_entries: config.max_entries, | ||
| max_entry_size: config.max_entry_size, | ||
| metrics_enabled: config.enable_metrics, | ||
| }) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think eventually we will move builing
DataStoreto the service factory