Skip to content

Commit

Permalink
Fold subsequent calls to limitador into a single one
Browse files Browse the repository at this point in the history
Signed-off-by: Eguzki Astiz Lezaun <[email protected]>
  • Loading branch information
eguzki committed Nov 21, 2024
1 parent b49133a commit 44e5837
Show file tree
Hide file tree
Showing 7 changed files with 872 additions and 9 deletions.
29 changes: 29 additions & 0 deletions src/auth_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,33 @@ mod test {
let auth_action = build_auth_action_with_predicates(Vec::default());
assert!(auth_action.conditions_apply());
}

#[test]
fn when_all_predicates_are_truthy_action_apply() {
let auth_action = build_auth_action_with_predicates(vec!["true".into(), "true".into()]);
assert!(auth_action.conditions_apply());
}

#[test]
fn when_not_all_predicates_are_truthy_action_does_not_apply() {
let auth_action = build_auth_action_with_predicates(vec![
"true".into(),
"true".into(),
"true".into(),
"false".into(),
]);
assert!(!auth_action.conditions_apply());
}

#[test]
#[should_panic]
fn when_a_cel_expression_does_not_evaluate_to_bool_panics() {
let auth_action = build_auth_action_with_predicates(vec![
"true".into(),
"true".into(),
"true".into(),
"1".into(),
]);
auth_action.conditions_apply();
}
}
71 changes: 71 additions & 0 deletions src/ratelimit_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl ConditionalData {
pub struct RateLimitAction {
grpc_service: Rc<GrpcService>,
scope: String,
service_name: String,
conditional_data_sets: Vec<ConditionalData>,
}

Expand All @@ -114,6 +115,7 @@ impl RateLimitAction {
Ok(Self {
grpc_service: Rc::new(GrpcService::new(Rc::new(service.clone()))),
scope: action.scope.clone(),
service_name: action.service.clone(),
conditional_data_sets: vec![ConditionalData::new(action)?],
})
}
Expand Down Expand Up @@ -148,6 +150,16 @@ impl RateLimitAction {
pub fn get_failure_mode(&self) -> FailureMode {
self.grpc_service.get_failure_mode()
}

#[must_use]
pub fn merge(&mut self, other: RateLimitAction) -> Option<RateLimitAction> {
if self.scope == other.scope && self.service_name == other.service_name {
self.conditional_data_sets
.extend(other.conditional_data_sets);
return None;
}
Some(other)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -185,6 +197,15 @@ mod test {
assert!(rl_action.conditions_apply());
}

#[test]
fn even_with_falsy_predicates_conditions_apply() {
let action = build_action(vec!["false".into()], Vec::default());
let service = build_service();
let rl_action = RateLimitAction::new(&action, &service)
.expect("action building failed. Maybe predicates compilation?");
assert!(rl_action.conditions_apply());
}

#[test]
fn empty_data_generates_empty_descriptor() {
let action = build_action(Vec::default(), Vec::default());
Expand Down Expand Up @@ -246,4 +267,54 @@ mod test {
.expect("action building failed. Maybe predicates compilation?");
assert_eq!(rl_action.build_descriptor(), RateLimitDescriptor::default());
}

#[test]
fn merged_actions_generate_descriptor_entries_for_truthy_predicates() {
let service = build_service();

let data_1 = vec![DataItem {
item: DataType::Expression(ExpressionItem {
key: "key_1".into(),
value: "'value_1'".into(),
}),
}];
let predicates_1 = vec!["true".into()];
let action_1 = build_action(predicates_1, data_1);
let mut rl_action_1 = RateLimitAction::new(&action_1, &service)
.expect("action building failed. Maybe predicates compilation?");

let data_2 = vec![DataItem {
item: DataType::Expression(ExpressionItem {
key: "key_2".into(),
value: "'value_2'".into(),
}),
}];
let predicates_2 = vec!["false".into()];
let action_2 = build_action(predicates_2, data_2);
let rl_action_2 = RateLimitAction::new(&action_2, &service)
.expect("action building failed. Maybe predicates compilation?");

let data_3 = vec![DataItem {
item: DataType::Expression(ExpressionItem {
key: "key_3".into(),
value: "'value_3'".into(),
}),
}];
let predicates_3 = vec!["true".into()];
let action_3 = build_action(predicates_3, data_3);
let rl_action_3 = RateLimitAction::new(&action_3, &service)
.expect("action building failed. Maybe predicates compilation?");

assert!(rl_action_1.merge(rl_action_2).is_none());
assert!(rl_action_1.merge(rl_action_3).is_none());

// it should generate descriptor entries from action 1 and action 3

let descriptor = rl_action_1.build_descriptor();
assert_eq!(descriptor.get_entries().len(), 2);
assert_eq!(descriptor.get_entries()[0].key, String::from("key_1"));
assert_eq!(descriptor.get_entries()[0].value, String::from("value_1"));
assert_eq!(descriptor.get_entries()[1].key, String::from("key_3"));
assert_eq!(descriptor.get_entries()[1].value, String::from("value_3"));
}
}
112 changes: 112 additions & 0 deletions src/runtime_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,116 @@ impl RuntimeAction {
pub fn get_service_type(&self) -> ServiceType {
self.grpc_service().get_service_type()
}

#[must_use]
pub fn merge(&mut self, other: RuntimeAction) -> Option<RuntimeAction> {
// only makes sense for rate limiting actions
if let Self::RateLimit(self_rl_action) = self {
if let Self::RateLimit(other_rl_action) = other {
return self_rl_action.merge(other_rl_action).map(Self::RateLimit);
}
}
Some(other)
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::configuration::{Action, FailureMode, ServiceType, Timeout};

fn build_rl_service() -> Service {
Service {
service_type: ServiceType::RateLimit,
endpoint: "limitador".into(),
failure_mode: FailureMode::default(),
timeout: Timeout::default(),
}
}

fn build_auth_service() -> Service {
Service {
service_type: ServiceType::Auth,
endpoint: "authorino".into(),
failure_mode: FailureMode::default(),
timeout: Timeout::default(),
}
}

fn build_action(service: &str, scope: &str) -> Action {
Action {
service: service.into(),
scope: scope.into(),
predicates: Vec::default(),
data: Vec::default(),
}
}

#[test]
fn only_rl_actions_are_merged() {
let mut services = HashMap::new();
services.insert(String::from("service_rl"), build_rl_service());

let rl_action_0 = build_action("service_rl", "scope");
let rl_action_1 = build_action("service_rl", "scope");

let mut rl_r_action_0 = RuntimeAction::new(&rl_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");
let rl_r_action_1 = RuntimeAction::new(&rl_action_1, &services)
.expect("action building failed. Maybe predicates compilation?");

assert!(rl_r_action_0.merge(rl_r_action_1).is_none());
}

#[test]
fn auth_actions_are_not_merged() {
let mut services = HashMap::new();
services.insert(String::from("service_auth"), build_auth_service());

let auth_action_0 = build_action("service_auth", "scope");
let auth_action_1 = build_action("service_auth", "scope");

let mut auth_r_action_0 = RuntimeAction::new(&auth_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");
let auth_r_action_1 = RuntimeAction::new(&auth_action_1, &services)
.expect("action building failed. Maybe predicates compilation?");

assert!(auth_r_action_0.merge(auth_r_action_1).is_some());
}

#[test]
fn auth_actions_do_not_merge_rl() {
let mut services = HashMap::new();
services.insert(String::from("service_rl"), build_rl_service());
services.insert(String::from("service_auth"), build_auth_service());

let rl_action_0 = build_action("service_rl", "scope");
let auth_action_0 = build_action("service_auth", "scope");

let mut rl_r_action_0 = RuntimeAction::new(&rl_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");

let auth_r_action_0 = RuntimeAction::new(&auth_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");

assert!(rl_r_action_0.merge(auth_r_action_0).is_some());
}

#[test]
fn rl_actions_do_not_merge_auth() {
let mut services = HashMap::new();
services.insert(String::from("service_rl"), build_rl_service());
services.insert(String::from("service_auth"), build_auth_service());

let rl_action_0 = build_action("service_rl", "scope");
let auth_action_0 = build_action("service_auth", "scope");

let rl_r_action_0 = RuntimeAction::new(&rl_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");

let mut auth_r_action_0 = RuntimeAction::new(&auth_action_0, &services)
.expect("action building failed. Maybe predicates compilation?");

assert!(auth_r_action_0.merge(rl_r_action_0).is_some());
}
}
Loading

0 comments on commit 44e5837

Please sign in to comment.