Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 41 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
name: Test
# -------------------------------------------------------------------
# ------------------------------- WARNING ---------------------------
# -------------------------------------------------------------------
#
# This file was automatically generated by gh-workflows using the
# gh-workflow-gen bin. You should add and commit this file to your
# git repository. **DO NOT EDIT THIS FILE BY HAND!** Any manual changes
# will be lost if the file is regenerated.
#
# To make modifications, update your `build.rs` configuration to adjust
# the workflow description as needed, then regenerate this file to apply
# those changes.
#
# -------------------------------------------------------------------
# ----------------------------- END WARNING -------------------------
# -------------------------------------------------------------------

name: Build and Test
on:
push:
branches:
- main
pull_request:

env:
CARGO_TERM_COLOR: always

- main
pull_request: {}
jobs:
cargo_build_and_test:
name: Cargo Build & Test
build:
name: Build and Test Rust SDK
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v4
with:
submodules: true
- run: npm ci
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose --all-targets
- run: cargo test --verbose
- run: cargo doc --verbose
- name: Checkout Code
uses: actions/checkout@v4
with:
submodules: 'true'
- run: npm ci
- name: Setup Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: clippy, rustfmt
- name: Cargo Build
run: cargo build --all-features --all-targets --verbose
- name: Cargo Test
run: cargo test --all-features --verbose
- name: Cargo Fmt
run: cargo fmt --check
- name: Cargo Clippy
run: cargo clippy --all-features --workspace -- -D warnings
- name: Cargo Doc
run: cargo doc --verbose
37 changes: 17 additions & 20 deletions eppo_core/src/configuration_fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ pub struct ConfigurationFetcherConfig {
pub sdk_metadata: SdkMetadata,
}

pub const DEFAULT_BASE_URL: &'static str = "https://fscdn.eppo.cloud/api";

const UFC_ENDPOINT: &'static str = "/flag-config/v1/config";
const BANDIT_ENDPOINT: &'static str = "/flag-config/v1/bandits";
pub const DEFAULT_BASE_URL: &str = "https://fscdn.eppo.cloud/api";
const UFC_ENDPOINT: &str = "/flag-config/v1/config";
const BANDIT_ENDPOINT: &str = "/flag-config/v1/bandits";

/// A client that fetches Eppo configuration from the server.
pub struct ConfigurationFetcher {
Expand Down Expand Up @@ -65,20 +64,19 @@ impl ConfigurationFetcher {
("coreVersion", env!("CARGO_PKG_VERSION")),
],
)
.map_err(|err| Error::InvalidBaseUrl(err))?;
.map_err(Error::InvalidBaseUrl)?;

log::debug!(target: "eppo", "fetching UFC flags configuration");
let response = self.client.get(url).send()?;

let response = response.error_for_status().map_err(|err| {
if err.status() == Some(StatusCode::UNAUTHORIZED) {
log::warn!(target: "eppo", "client is not authorized. Check your API key");
self.unauthorized = true;
return Error::Unauthorized;
} else {
log::warn!(target: "eppo", "received non-200 response while fetching new configuration: {:?}", err);
return Error::from(err);

log::warn!(target: "eppo", "client is not authorized. Check your API key");
self.unauthorized = true;
Error::Unauthorized
} else {
log::warn!(target: "eppo", "received non-200 response while fetching new configuration: {:?}", err);
Error::from(err)
}
})?;

Expand All @@ -100,20 +98,19 @@ impl ConfigurationFetcher {
("coreVersion", env!("CARGO_PKG_VERSION")),
],
)
.map_err(|err| Error::InvalidBaseUrl(err))?;
.map_err(Error::InvalidBaseUrl)?;

log::debug!(target: "eppo", "fetching UFC bandits configuration");
let response = self.client.get(url).send()?;

let response = response.error_for_status().map_err(|err| {
if err.status() == Some(StatusCode::UNAUTHORIZED) {
log::warn!(target: "eppo", "client is not authorized. Check your API key");
self.unauthorized = true;
return Error::Unauthorized;
} else {
log::warn!(target: "eppo", "received non-200 response while fetching new configuration: {:?}", err);
return Error::from(err);

log::warn!(target: "eppo", "client is not authorized. Check your API key");
self.unauthorized = true;
Error::Unauthorized
} else {
log::warn!(target: "eppo", "received non-200 response while fetching new configuration: {:?}", err);
Error::from(err)
}
})?;

Expand Down
6 changes: 3 additions & 3 deletions eppo_core/src/eval/eval_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ pub(super) fn get_assignment_with_visitor<V: EvalAssignmentVisitor>(

config.flags.compiled.eval_flag(
visitor,
&flag_key,
&subject_key,
&subject_attributes,
flag_key,
subject_key,
subject_attributes,
expected_type,
now,
)
Expand Down
13 changes: 8 additions & 5 deletions eppo_core/src/eval/eval_bandits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub struct BanditResult {

/// Evaluate the specified string feature flag for the given subject. If resulting variation is
/// a bandit, evaluate the bandit to return the action.
#[allow(clippy::too_many_arguments)]
pub fn get_bandit_action(
configuration: Option<&Configuration>,
flag_key: &str,
Expand All @@ -73,6 +74,7 @@ pub fn get_bandit_action(

/// Evaluate the specified string feature flag for the given subject. If resulting variation is
/// a bandit, evaluate the bandit to return the action. In addition, return evaluation details.
#[allow(clippy::too_many_arguments)]
pub fn get_bandit_action_details(
configuration: Option<&Configuration>,
flag_key: &str,
Expand Down Expand Up @@ -106,6 +108,7 @@ pub fn get_bandit_action_details(

/// Evaluate the specified string feature flag for the given subject. If resulting variation is
/// a bandit, evaluate the bandit to return the action.
#[allow(clippy::too_many_arguments)]
fn get_bandit_action_with_visitor<V: EvalBanditVisitor>(
visitor: &mut V,
configuration: Option<&Configuration>,
Expand Down Expand Up @@ -232,7 +235,7 @@ fn get_bandit_action_with_visitor<V: EvalBanditVisitor>(
bandit_event: Some(bandit_event),
};
visitor.on_result(Ok(()), &result);
return result;
result
}

impl BanditModelData {
Expand All @@ -246,7 +249,7 @@ impl BanditModelData {
// total_shards is not configurable at the moment.
const TOTAL_SHARDS: u32 = 10_000;

if actions.len() == 0 {
if actions.is_empty() {
return Err(EvaluationFailure::NoActionsSuppliedForBandit);
}

Expand Down Expand Up @@ -360,7 +363,7 @@ impl BanditModelData {

coefficients.intercept
+ score_attributes(
&action.attributes,
action.attributes,
&coefficients.action_numeric_coefficients,
&coefficients.action_categorical_coefficients,
)
Expand All @@ -378,7 +381,7 @@ fn score_attributes(
categorical_coefficients: &[BanditCategoricalAttributeCoefficient],
) -> f64 {
numeric_coefficients
.into_iter()
.iter()
.map(|coef| {
attributes
.numeric
Expand All @@ -388,7 +391,7 @@ fn score_attributes(
.map(|value| value * coef.coefficient)
.unwrap_or(coef.missing_value_coefficient)
})
.chain(categorical_coefficients.into_iter().map(|coef| {
.chain(categorical_coefficients.iter().map(|coef| {
attributes
.categorical
.get(&coef.attribute_key)
Expand Down
19 changes: 10 additions & 9 deletions eppo_core/src/eval/eval_details_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,33 +139,34 @@ impl EvalDetailsBuilder {
}
EvaluationFailure::Error(EvaluationError::UnexpectedConfigurationError)
| EvaluationFailure::Error(EvaluationError::UnexpectedConfigurationParseError) => {
format!("Configuration error. This might indicate that you're using an outdated version of Eppo SDK")
"Configuration error. This might indicate that you're using an outdated version of Eppo SDK".to_string()
}
EvaluationFailure::ConfigurationMissing => {
format!("Configuration has not been fetched yet")
"Configuration has not been fetched yet".to_string()
}
EvaluationFailure::FlagUnrecognizedOrDisabled => {
format!("Unrecognized or disabled flag: {}", self.flag_key)
}
EvaluationFailure::FlagDisabled => {
format!("Unrecognized or disabled flag: {}", self.flag_key)
}
EvaluationFailure::DefaultAllocationNull => format!(
EvaluationFailure::DefaultAllocationNull => {
"No allocations matched. Falling back to \"Default Allocation\", serving NULL"
),
.to_string()
}
EvaluationFailure::NonBanditVariation => {
debug_assert!(
false,
"{failure:?} should never be emitted by flag evaluation"
);
format!("Flag evaluated to a non-bandit allocation")
"Flag evaluated to a non-bandit allocation".to_string()
}
EvaluationFailure::NoActionsSuppliedForBandit => {
debug_assert!(
false,
"{failure:?} should never be emitted by flag evaluation"
);
format!("No actions were supplied for bandit evaluation")
"No actions were supplied for bandit evaluation".to_string()
}
};
}
Expand Down Expand Up @@ -219,7 +220,7 @@ impl EvalBanditVisitor for EvalDetailsBuilder {
self.bandit_key = Some(key.to_owned());
}

fn visit_assignment<'a>(&'a mut self) -> Self::AssignmentVisitor<'a> {
fn visit_assignment(&mut self) -> Self::AssignmentVisitor<'_> {
self
}

Expand All @@ -234,7 +235,7 @@ impl<'b> EvalAssignmentVisitor for &'b mut EvalDetailsBuilder {
<EvalDetailsBuilder as EvalAssignmentVisitor>::AllocationVisitor<'a>
where Self: 'a;

fn visit_allocation<'a>(&'a mut self, allocation: &Allocation) -> Self::AllocationVisitor<'a> {
fn visit_allocation(&mut self, allocation: &Allocation) -> Self::AllocationVisitor<'_> {
EvalAssignmentVisitor::visit_allocation(*self, allocation)
}

Expand All @@ -256,7 +257,7 @@ impl EvalAssignmentVisitor for EvalDetailsBuilder {
where
Self: 'a;

fn visit_allocation<'a>(&'a mut self, allocation: &Allocation) -> Self::AllocationVisitor<'a> {
fn visit_allocation(&mut self, allocation: &Allocation) -> Self::AllocationVisitor<'_> {
let order_position = self.allocation_eval_results.len() + 1;
let result = self
.allocation_eval_results
Expand Down
6 changes: 3 additions & 3 deletions eppo_core/src/eval/eval_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(super) trait EvalBanditVisitor {
/// Called when (if) evaluation gets configuration.
fn on_configuration(&mut self, configuration: &Configuration);

fn visit_assignment<'a>(&'a mut self) -> Self::AssignmentVisitor<'a>;
fn visit_assignment(&mut self) -> Self::AssignmentVisitor<'_>;

/// Called when bandit key is known.
fn on_bandit_key(&mut self, key: &str);
Expand Down Expand Up @@ -98,7 +98,7 @@ impl EvalBanditVisitor for NoopEvalVisitor {
fn on_bandit_key(&mut self, _key: &str) {}

#[inline]
fn visit_assignment<'a>(&'a mut self) -> NoopEvalVisitor {
fn visit_assignment(&mut self) -> NoopEvalVisitor {
NoopEvalVisitor
}

Expand All @@ -110,7 +110,7 @@ impl EvalAssignmentVisitor for NoopEvalVisitor {
type AllocationVisitor<'a> = NoopEvalVisitor;

#[inline]
fn visit_allocation<'a>(&'a mut self, _allocation: &Allocation) -> Self::AllocationVisitor<'a> {
fn visit_allocation(&mut self, _allocation: &Allocation) -> Self::AllocationVisitor<'_> {
NoopEvalVisitor
}

Expand Down
12 changes: 6 additions & 6 deletions eppo_core/src/eval/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ impl Evaluator {
let config = self.get_configuration();
get_assignment(
config.as_ref().map(AsRef::as_ref),
&flag_key,
&subject_key,
&subject_attributes,
flag_key,
subject_key,
subject_attributes,
expected_type,
Utc::now(),
)
Expand All @@ -62,9 +62,9 @@ impl Evaluator {
let config = self.get_configuration();
get_assignment_details(
config.as_ref().map(AsRef::as_ref),
&flag_key,
&subject_key,
&subject_attributes,
flag_key,
subject_key,
subject_attributes,
expected_type,
Utc::now(),
)
Expand Down
2 changes: 1 addition & 1 deletion eppo_core/src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod pyo3_impl {

impl ToPyObject for Str {
fn to_object(&self, py: Python<'_>) -> PyObject {
PyString::new_bound(py, &self).into()
PyString::new_bound(py, self).into()
}
}
}
Loading
Loading