Skip to content

Commit 0cec63b

Browse files
authored
feat: Introduce ConfigurationProvider trait (#74)
I want to hide the concept of a `Configuration` as an implementation detail and instead introduce a trait (`ConfigurationProvider`) that offers the functionality to provide features and properties. This trait will then be implemented by many of the structs we already have: app-configuration clients, the `Configuration` itself,... and it will be easier to mock (/dependency injection). I also think this will help us to narrow the API of the crate: we can hide `Configuration` and some other structs together with it. --------- Signed-off-by: Javier G. Sogo <[email protected]>
1 parent c417b8d commit 0cec63b

15 files changed

+166
-180
lines changed

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2025-05-05T10:55:39Z",
6+
"generated_at": "2025-05-08T08:31:42Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -81,7 +81,7 @@
8181
{
8282
"hashed_secret": "fb34629c9af1ed4045b5d6f287426276b2be3a1e",
8383
"is_verified": false,
84-
"line_number": 46,
84+
"line_number": 44,
8585
"type": "Secret Keyword",
8686
"verified_result": null
8787
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Create your client with the context (environment and collection) you want to con
3030

3131
```rust
3232
use appconfiguration::{
33-
AppConfigurationClient, AppConfigurationClientIBMCloud,
33+
ConfigurationProvider, AppConfigurationClientIBMCloud,
3434
ConfigurationId, Entity, Result, Value, Feature, OfflineMode
3535
};
3636

src/client/app_configuration_client.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use crate::Result;
16-
1715
use crate::client::feature_proxy::FeatureProxy;
1816
use crate::client::feature_snapshot::FeatureSnapshot;
1917
use crate::client::property_proxy::PropertyProxy;
2018
use crate::client::property_snapshot::PropertySnapshot;
21-
19+
use crate::Result;
2220
/// Identifies a configuration
2321
#[derive(Debug, Clone)]
2422
pub struct ConfigurationId {
@@ -40,12 +38,11 @@ impl ConfigurationId {
4038
}
4139
}
4240

43-
/// AppConfiguration client for browsing, and evaluating features and properties.
44-
pub trait AppConfigurationClient {
41+
pub trait ConfigurationProvider {
4542
/// Returns the list of features.
4643
///
47-
/// The list contains the `id`s that can be used in [`get_feature`](AppConfigurationClient::get_feature)
48-
/// or [`get_feature_proxy`](AppConfigurationClient::get_feature_proxy) to retrieve the actual features.
44+
/// The list contains the `id`s that can be used in other methods to return
45+
/// concrete features, like [`get_feature`](ConfigurationProvider::get_feature).
4946
fn get_feature_ids(&self) -> Result<Vec<String>>;
5047

5148
/// Returns a snapshot for a [`Feature`](crate::Feature).
@@ -55,16 +52,10 @@ pub trait AppConfigurationClient {
5552
/// will be received from the server.
5653
fn get_feature(&self, feature_id: &str) -> Result<FeatureSnapshot>;
5754

58-
/// Returns a proxied [`Feature`](crate::Feature).
59-
///
60-
/// This proxied feature will envaluate entities using the latest information
61-
/// available if the client implementation support some kind of live-updates.
62-
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>>;
63-
6455
/// Returns the list of properties.
6556
///
66-
/// The list contains the `id`s that can be used in [`get_property`](AppConfigurationClient::get_property)
67-
/// or [`get_property_proxy`](AppConfigurationClient::get_property_proxy) to retrieve the actual features.
57+
/// The list contains the `id`s that can be used in other methods to return
58+
/// concrete properties, like [`get_property`](ConfigurationProvider::get_property).
6859
fn get_property_ids(&self) -> Result<Vec<String>>;
6960

7061
/// Returns a snapshot for a [`Property`](crate::Property).
@@ -73,10 +64,32 @@ pub trait AppConfigurationClient {
7364
/// will always evaluate the same entities to the same values, no updates
7465
/// will be received from the server
7566
fn get_property(&self, property_id: &str) -> Result<PropertySnapshot>;
67+
}
68+
69+
/// AppConfiguration client for browsing, and evaluating features and properties.
70+
pub trait AppConfigurationClient: ConfigurationProvider {
71+
/// Returns a proxied [`Feature`](crate::Feature).
72+
///
73+
/// This proxied feature will envaluate entities using the latest information
74+
/// available if the client implementation support some kind of live-updates.
75+
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>>;
7676

7777
/// Returns a proxied [`Property`](crate::Property).
7878
///
7979
/// This proxied property will envaluate entities using the latest information
8080
/// available if the client implementation support some kind of live-updates.
8181
fn get_property_proxy(&self, property_id: &str) -> Result<PropertyProxy>;
8282
}
83+
84+
impl<T: ConfigurationProvider> AppConfigurationClient for T {
85+
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>> {
86+
// FIXME: there is and was no validation happening if the feature exists.
87+
// Comments and error messages in FeatureProxy suggest that this should happen here.
88+
// same applies for properties.
89+
Ok(FeatureProxy::new(self, feature_id.to_string()))
90+
}
91+
92+
fn get_property_proxy(&self, property_id: &str) -> Result<PropertyProxy> {
93+
Ok(PropertyProxy::new(self, property_id.to_string()))
94+
}
95+
}

src/client/app_configuration_http.rs

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,15 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
pub use crate::client::feature_proxy::FeatureProxy;
1615
use crate::client::feature_snapshot::FeatureSnapshot;
17-
pub use crate::client::property_proxy::PropertyProxy;
1816
use crate::client::property_snapshot::PropertySnapshot;
1917
use crate::errors::Result;
2018

2119
use crate::network::live_configuration::{CurrentMode, LiveConfiguration, LiveConfigurationImpl};
2220
use crate::network::{ServiceAddress, TokenProvider};
23-
use crate::{OfflineMode, ServerClientImpl};
21+
use crate::{ConfigurationProvider, OfflineMode, ServerClientImpl};
2422

25-
use super::{AppConfigurationClient, ConfigurationId};
23+
use super::ConfigurationId;
2624

2725
/// AppConfiguration client implementation that connects to a server
2826
#[derive(Debug)]
@@ -62,48 +60,21 @@ impl<T: LiveConfiguration> AppConfigurationClientHttp<T> {
6260
}
6361
}
6462

65-
impl<T: LiveConfiguration> AppConfigurationClient for AppConfigurationClientHttp<T> {
63+
impl<T: LiveConfiguration> ConfigurationProvider for AppConfigurationClientHttp<T> {
6664
fn get_feature_ids(&self) -> Result<Vec<String>> {
67-
Ok(self
68-
.live_configuration
69-
.get_configuration()?
70-
.get_feature_ids()
71-
.into_iter()
72-
.cloned()
73-
.collect())
65+
self.live_configuration.get_feature_ids()
7466
}
7567

7668
fn get_feature(&self, feature_id: &str) -> Result<FeatureSnapshot> {
77-
self.live_configuration
78-
.get_configuration()?
79-
.get_feature(feature_id)
80-
}
81-
82-
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>> {
83-
// FIXME: there is and was no validation happening if the feature exists.
84-
// Comments and error messages in FeatureProxy suggest that this should happen here.
85-
// same applies for properties.
86-
Ok(FeatureProxy::new(self, feature_id.to_string()))
69+
self.live_configuration.get_feature(feature_id)
8770
}
8871

8972
fn get_property_ids(&self) -> Result<Vec<String>> {
90-
Ok(self
91-
.live_configuration
92-
.get_configuration()?
93-
.get_property_ids()
94-
.into_iter()
95-
.cloned()
96-
.collect())
73+
self.live_configuration.get_property_ids()
9774
}
9875

9976
fn get_property(&self, property_id: &str) -> Result<PropertySnapshot> {
100-
self.live_configuration
101-
.get_configuration()?
102-
.get_property(property_id)
103-
}
104-
105-
fn get_property_proxy(&self, property_id: &str) -> Result<PropertyProxy> {
106-
Ok(PropertyProxy::new(self, property_id.to_string()))
77+
self.live_configuration.get_property(property_id)
10778
}
10879
}
10980

@@ -122,11 +93,24 @@ mod tests {
12293
struct LiveConfigurationMock {
12394
configuration: Configuration,
12495
}
125-
impl LiveConfiguration for LiveConfigurationMock {
126-
fn get_configuration(&self) -> crate::network::live_configuration::Result<Configuration> {
127-
Ok(self.configuration.clone())
96+
impl ConfigurationProvider for LiveConfigurationMock {
97+
fn get_feature_ids(&self) -> Result<Vec<String>> {
98+
self.configuration.get_feature_ids()
99+
}
100+
101+
fn get_feature(&self, feature_id: &str) -> Result<FeatureSnapshot> {
102+
self.configuration.get_feature(feature_id)
103+
}
104+
105+
fn get_property_ids(&self) -> Result<Vec<String>> {
106+
self.configuration.get_property_ids()
128107
}
129108

109+
fn get_property(&self, property_id: &str) -> Result<PropertySnapshot> {
110+
self.configuration.get_property(property_id)
111+
}
112+
}
113+
impl LiveConfiguration for LiveConfigurationMock {
130114
fn get_thread_status(
131115
&mut self,
132116
) -> ThreadStatus<crate::network::live_configuration::Result<()>> {

src/client/app_configuration_ibm_cloud.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,15 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
pub use crate::client::feature_proxy::FeatureProxy;
1615
use crate::client::feature_snapshot::FeatureSnapshot;
17-
pub use crate::client::property_proxy::PropertyProxy;
1816
use crate::client::property_snapshot::PropertySnapshot;
1917
use crate::errors::Result;
2018
use crate::network::live_configuration::LiveConfigurationImpl;
2119
use crate::network::ServiceAddress;
22-
use crate::{IBMCloudTokenProvider, OfflineMode};
20+
use crate::{ConfigurationProvider, IBMCloudTokenProvider, OfflineMode};
2321

2422
use super::AppConfigurationClientHttp;
25-
use super::{AppConfigurationClient, ConfigurationId};
23+
use super::ConfigurationId;
2624

2725
/// AppConfiguration client connection to IBM Cloud.
2826
#[derive(Debug)]
@@ -69,7 +67,7 @@ impl AppConfigurationClientIBMCloud {
6967
}
7068
}
7169

72-
impl AppConfigurationClient for AppConfigurationClientIBMCloud {
70+
impl ConfigurationProvider for AppConfigurationClientIBMCloud {
7371
fn get_feature_ids(&self) -> Result<Vec<String>> {
7472
self.client.get_feature_ids()
7573
}
@@ -78,21 +76,13 @@ impl AppConfigurationClient for AppConfigurationClientIBMCloud {
7876
self.client.get_feature(feature_id)
7977
}
8078

81-
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>> {
82-
self.client.get_feature_proxy(feature_id)
83-
}
84-
8579
fn get_property_ids(&self) -> Result<Vec<String>> {
8680
self.client.get_property_ids()
8781
}
8882

8983
fn get_property(&self, property_id: &str) -> Result<PropertySnapshot> {
9084
self.client.get_property(property_id)
9185
}
92-
93-
fn get_property_proxy(&self, property_id: &str) -> Result<PropertyProxy> {
94-
self.client.get_property_proxy(property_id)
95-
}
9686
}
9787

9888
#[cfg(test)]

src/client/app_configuration_offline.rs

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@
1313
// limitations under the License.
1414

1515
use crate::client::configuration::Configuration;
16-
pub use crate::client::feature_proxy::FeatureProxy;
16+
1717
use crate::client::feature_snapshot::FeatureSnapshot;
18-
pub use crate::client::property_proxy::PropertyProxy;
1918
use crate::client::property_snapshot::PropertySnapshot;
2019
use crate::errors::Result;
2120
use crate::models::ConfigurationJson;
22-
23-
use super::AppConfigurationClient;
21+
use crate::ConfigurationProvider;
2422

2523
/// AppConfiguration client using a local file with a configuration snapshot
2624
#[derive(Debug)]
@@ -42,41 +40,20 @@ impl AppConfigurationOffline {
4240
}
4341
}
4442

45-
impl AppConfigurationClient for AppConfigurationOffline {
43+
impl ConfigurationProvider for AppConfigurationOffline {
4644
fn get_feature_ids(&self) -> Result<Vec<String>> {
47-
Ok(self
48-
.config_snapshot
49-
.get_feature_ids()
50-
.into_iter()
51-
.cloned()
52-
.collect())
45+
self.config_snapshot.get_feature_ids()
5346
}
5447

5548
fn get_feature(&self, feature_id: &str) -> Result<FeatureSnapshot> {
5649
self.config_snapshot.get_feature(feature_id)
5750
}
5851

59-
fn get_feature_proxy<'a>(&'a self, feature_id: &str) -> Result<FeatureProxy<'a>> {
60-
// FIXME: there is and was no validation happening if the feature exists.
61-
// Comments and error messages in FeatureProxy suggest that this should happen here.
62-
// same applies for properties.
63-
Ok(FeatureProxy::new(self, feature_id.to_string()))
64-
}
65-
6652
fn get_property_ids(&self) -> Result<Vec<String>> {
67-
Ok(self
68-
.config_snapshot
69-
.get_property_ids()
70-
.into_iter()
71-
.cloned()
72-
.collect())
53+
self.config_snapshot.get_property_ids()
7354
}
7455

7556
fn get_property(&self, property_id: &str) -> Result<PropertySnapshot> {
7657
self.config_snapshot.get_property(property_id)
7758
}
78-
79-
fn get_property_proxy(&self, property_id: &str) -> Result<PropertyProxy> {
80-
Ok(PropertyProxy::new(self, property_id.to_string()))
81-
}
8259
}

0 commit comments

Comments
 (0)