Skip to content

Commit 2b104b8

Browse files
committed
feat: ofrep provider
Signed-off-by: Rahul Baradol <[email protected]>
1 parent 5c0fed4 commit 2b104b8

File tree

5 files changed

+675
-2
lines changed

5 files changed

+675
-2
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ edition = "2021"
1212
members = [
1313
"crates/env-var",
1414
"crates/flagd",
15-
"crates/flipt"
16-
]
15+
"crates/flipt",
16+
"crates/ofrep"
17+
]

crates/ofrep/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "ofrep"
3+
version = "0.0.1"
4+
edition = "2024"
5+
6+
[dependencies]
7+
async-trait = "0.1.88"
8+
open-feature = "0.2.5"
9+
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
10+
serde = "1.0.219"
11+
serde_json = "1.0.140"
12+
tracing = "0.1.41"
13+
thiserror = "2.0"
14+
anyhow = "1.0.98"

crates/ofrep/src/error.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
pub enum OfrepError {
5+
#[error("Provider error: {0}")]
6+
Provider(String),
7+
#[error("Connection error: {0}")]
8+
Connection(String),
9+
#[error("Invalid configuration: {0}")]
10+
Config(String),
11+
}
12+
13+
// Add implementations for error conversion
14+
impl From<Box<dyn std::error::Error>> for OfrepError {
15+
fn from(error: Box<dyn std::error::Error>) -> Self {
16+
OfrepError::Provider(error.to_string())
17+
}
18+
}
19+
20+
impl From<Box<dyn std::error::Error + Send + Sync>> for OfrepError {
21+
fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
22+
OfrepError::Provider(error.to_string())
23+
}
24+
}
25+
26+
impl From<anyhow::Error> for OfrepError {
27+
fn from(error: anyhow::Error) -> Self {
28+
OfrepError::Provider(error.to_string())
29+
}
30+
}

crates/ofrep/src/lib.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
mod error;
2+
mod resolver;
3+
4+
use error::OfrepError;
5+
use open_feature::provider::{FeatureProvider, ProviderMetadata, ResolutionDetails};
6+
use open_feature::{EvaluationContext, EvaluationError, StructValue};
7+
use reqwest::header::HeaderMap;
8+
use resolver::Resolver;
9+
use std::sync::Arc;
10+
use std::time::Duration;
11+
use tracing::debug;
12+
use tracing::instrument;
13+
14+
use async_trait::async_trait;
15+
16+
const DEFAULT_BASE_URL: &str = "http://localhost:8016";
17+
const DEFAULT_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
18+
19+
#[derive(Debug, Clone)]
20+
pub struct OfrepOptions {
21+
pub base_url: String,
22+
pub headers: HeaderMap,
23+
pub connect_timeout: Duration,
24+
}
25+
26+
impl Default for OfrepOptions {
27+
fn default() -> Self {
28+
OfrepOptions {
29+
base_url: DEFAULT_BASE_URL.to_string(),
30+
headers: HeaderMap::new(),
31+
connect_timeout: DEFAULT_CONNECT_TIMEOUT,
32+
}
33+
}
34+
}
35+
36+
pub struct OfrepProvider {
37+
provider: Arc<dyn FeatureProvider + Send + Sync>,
38+
}
39+
40+
impl OfrepProvider {
41+
#[instrument(skip(options))]
42+
pub async fn new(options: OfrepOptions) -> Result<Self, OfrepError> {
43+
debug!("Initializing OfrepProvider with options: {:?}", options);
44+
Ok(Self {
45+
provider: Arc::new(Resolver::new(&options)),
46+
})
47+
}
48+
}
49+
50+
#[async_trait]
51+
impl FeatureProvider for OfrepProvider {
52+
fn metadata(&self) -> &ProviderMetadata {
53+
self.provider.metadata()
54+
}
55+
56+
async fn resolve_bool_value(
57+
&self,
58+
flag_key: &str,
59+
context: &EvaluationContext,
60+
) -> Result<ResolutionDetails<bool>, EvaluationError> {
61+
let result = self.provider.resolve_bool_value(flag_key, context).await?;
62+
Ok(result)
63+
}
64+
65+
async fn resolve_int_value(
66+
&self,
67+
flag_key: &str,
68+
context: &EvaluationContext,
69+
) -> Result<ResolutionDetails<i64>, EvaluationError> {
70+
let result = self.provider.resolve_int_value(flag_key, context).await?;
71+
Ok(result)
72+
}
73+
74+
async fn resolve_float_value(
75+
&self,
76+
flag_key: &str,
77+
context: &EvaluationContext,
78+
) -> Result<ResolutionDetails<f64>, EvaluationError> {
79+
let result = self.provider.resolve_float_value(flag_key, context).await?;
80+
Ok(result)
81+
}
82+
83+
async fn resolve_string_value(
84+
&self,
85+
flag_key: &str,
86+
context: &EvaluationContext,
87+
) -> Result<ResolutionDetails<String>, EvaluationError> {
88+
let result = self
89+
.provider
90+
.resolve_string_value(flag_key, context)
91+
.await?;
92+
Ok(result)
93+
}
94+
95+
async fn resolve_struct_value(
96+
&self,
97+
flag_key: &str,
98+
context: &EvaluationContext,
99+
) -> Result<ResolutionDetails<StructValue>, EvaluationError> {
100+
let result = self
101+
.provider
102+
.resolve_struct_value(flag_key, context)
103+
.await?;
104+
Ok(result)
105+
}
106+
}

0 commit comments

Comments
 (0)