Skip to content

Commit 554cf22

Browse files
sheepdukejustinabrahmsdaiyue-microsoft
authored
feat!: re-design No-op Provider (#56)
Signed-off-by: YUE Daian <sheepduke@gmail.com> Signed-off-by: YUE Daian <sheepduke@users.noreply.github.com> Signed-off-by: Daian YUE 🐏 <daiyue@microsoft.com> Co-authored-by: Justin Abrahms <justin@abrah.ms> Co-authored-by: Daian YUE 🐏 <daiyue@microsoft.com>
1 parent 78b0bf3 commit 554cf22

File tree

12 files changed

+284
-512
lines changed

12 files changed

+284
-512
lines changed

.github/workflows/rust.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,15 @@ jobs:
2121

2222
steps:
2323
- uses: actions/checkout@v4
24-
- name: Build
25-
run: cargo build --verbose
24+
2625
- name: Check formats
2726
run: cargo fmt --check
27+
28+
- name: Check code quality
29+
run: cargo clippy -- -D warnings
30+
31+
- name: Build
32+
run: cargo build --verbose
33+
2834
- name: Run tests
2935
run: cargo test --verbose

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ maintenance = { status = "actively-developed" }
1818
[dependencies]
1919
async-trait = "0.1.77"
2020
lazy_static = "1.4.0"
21+
mockall = { version = "0.12.1", optional = true }
2122
serde_json = { version = "1.0.111", optional = true }
2223
time = "0.3.31"
2324
tokio = { version = "1.35.1", features = [ "full" ] }
@@ -27,4 +28,6 @@ typed-builder = "0.18.0"
2728
spec = { path = "spec" }
2829

2930
[features]
30-
serde_json = ["dep:serde_json"]
31+
default = [ "test-util" ]
32+
test-util = [ "dep:mockall" ]
33+
serde_json = [ "dep:serde_json" ]

README.md

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -63,49 +63,47 @@ open-feature = "0.1.5"
6363
```rust
6464
async fn example() -> Result<(), Error> {
6565
// Acquire an OpenFeature API instance.
66+
// Note the `await` call here because asynchronous lock is used to
67+
// guarantee thread safety.
6668
let mut api = OpenFeature::singleton_mut().await;
6769

68-
// configure a provider
69-
api.set_provider(NoOpProvider::new())
70-
.await;
70+
// Configure a provider.
71+
// By default [`NoOpProvider`] is used.
72+
api.set_provider(NoOpProvider::default()).await;
7173

7274
// create a client
7375
let client = api.get_client();
7476

7577
// get a bool flag value
7678
let is_feature_enabled = client
77-
.get_bool_value("v2_enabled", false, None)
79+
.get_bool_value("v2_enabled", None, None)
80+
.unwrap_or(false)
7881
.await;
7982

8083
Ok(())
8184
}
8285
```
8386

87+
Note that the default `NoOpProvider` always returns `Err` for any given input.
88+
8489
#### Extended Example
8590

8691
```rust
87-
#[derive(Clone, Default, Debug)]
88-
struct MyStruct {}
89-
9092
#[tokio::test]
9193
async fn extended_example() {
9294
// Acquire an OpenFeature API instance.
93-
// Note the `await` call here because asynchronous lock is used to guarantee thread safety.
9495
let mut api = OpenFeature::singleton_mut().await;
9596

96-
api.set_provider(NoOpProvider::builder().int_value(100).build())
97-
.await;
97+
// Set the default (unnamed) provider.
98+
api.set_provider(NoOpProvider::default()).await;
9899

99100
// Create an unnamed client.
100101
let client = api.create_client();
101102

102103
// Create an evaluation context.
103104
// It supports types mentioned in the specification.
104-
//
105-
// You have multiple ways to add a custom field.
106-
let evaluation_context = EvaluationContext::builder()
107-
.targeting_key("Targeting")
108-
.build()
105+
let evaluation_context = EvaluationContext::default()
106+
.with_targeting_key("Targeting")
109107
.with_custom_field("bool_key", true)
110108
.with_custom_field("int_key", 100)
111109
.with_custom_field("float_key", 3.14)
@@ -121,39 +119,18 @@ async fn extended_example() {
121119
EvaluationContextFieldValue::new_struct(MyStruct::default()),
122120
);
123121

124-
// This function returns a `Result`. You can process it with functions provided by std.
122+
// This function returns a `Result`.
123+
// You can process it with functions provided by std.
125124
let is_feature_enabled = client
126125
.get_bool_value("SomeFlagEnabled", Some(&evaluation_context), None)
127126
.await
128127
.unwrap_or(false);
129128

130129
if is_feature_enabled {
131-
// Do something.
132-
}
133-
134-
// Let's get evaluation details.
135-
let result = client
136-
.get_int_details(
137-
"key",
138-
Some(&EvaluationContext::default().with_custom_field("some_key", "some_value")),
139-
None,
140-
)
141-
.await;
142-
143-
match result {
144-
Ok(details) => {
145-
assert_eq!(details.value, 100);
146-
assert_eq!(details.reason, Some(EvaluationReason::Static));
147-
assert_eq!(details.variant, Some("Static".to_string()));
148-
assert_eq!(details.flag_metadata.values.iter().count(), 2);
149-
}
150-
Err(error) => {
151-
println!(
152-
"Error: {}\nMessage: {:?}\n",
153-
error.code.to_string(),
154-
error.message
155-
);
156-
}
130+
// Let's get evaluation details.
131+
let _result = client
132+
.get_int_details("key", Some(&evaluation_context), None)
133+
.await;
157134
}
158135
}
159136
```
@@ -195,9 +172,8 @@ Once you've added a provider as a dependency, it can be registered with OpenFeat
195172
// Set the default feature provider. Please replace the `NoOpProvider` with the one you want.
196173
// If you do not do that, [`NoOpProvider`] will be used by default.
197174
//
198-
// By default, [`NoOpProvider`] will simply return the default value of each type.
199-
// You can inject value you want via its builder or evaluation context. See other sections
200-
// for more details.
175+
// [`NoOpProvider`] always returns `Err` despite any input. You can use functions like
176+
// `unwrap_or()` to specify default values.
201177
//
202178
// If you set a new provider after creating some clients, the existing clients will pick up
203179
// the new provider you just set.
@@ -230,7 +206,7 @@ client.set_evaluation_context(client_evaluation_context);
230206
// Pass evaluation context in evaluation functions.
231207
// This one will overwrite the globla evaluation context and
232208
// the client level one.
233-
client.get_int_value("flag", &evaluation_context, None);
209+
client.get_int_value("flag", Some(&evaluation_context), None);
234210
```
235211

236212
### Hooks
@@ -259,10 +235,7 @@ If a name has no associated provider, the global provider is used.
259235

260236
```rust
261237
// Create a named provider and bind it.
262-
api.set_named_provider(
263-
"named",
264-
NoOpProvider::builder().int_value(42).build())
265-
.await;
238+
api.set_named_provider("named", NoOpProvider::default()).await;
266239

267240
// This named client will use the feature provider bound to this name.
268241
let client = api.create_named_client("named");

0 commit comments

Comments
 (0)