Skip to content

Commit 830e2ef

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

File tree

5 files changed

+696
-2
lines changed

5 files changed

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

0 commit comments

Comments
 (0)