Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Commit

Permalink
Invalidation handles (#419)
Browse files Browse the repository at this point in the history
* WIP

* WIP

* Refactored structure while maintaining the old behaviour

* rustfmt

* Added post endpoint

* Tested Tokens and Contracts payload

* Renamed struct

* Added unit tests for invalidation_patterns and invalidation_scope mappers

* Adjusted transactions invalidation pattern

* Renamed internal variable

* Re-ordered match branches

* Moved the consts to the module root

* Changed return type and ordering of params

* Added transfer variant to InvalidationPatterns
  • Loading branch information
jpalvarezl authored Jun 8, 2021
1 parent ed4cb16 commit 40296f7
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 45 deletions.
17 changes: 2 additions & 15 deletions src/cache/cache_op_executors.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
use crate::cache::cache_operations::{CacheResponse, InvalidationPattern, RequestCached};
use crate::cache::inner_cache::CachedWithCode;
use crate::cache::Cache;
use crate::providers::info::TOKENS_KEY;
use crate::cache::{Cache, CACHE_REQS_PREFIX, CACHE_RESP_PREFIX};
use crate::utils::errors::{ApiError, ApiResult};
use rocket::response::content;
use serde::Serialize;
use std::time::Duration;

const CACHE_REQS_PREFIX: &'static str = "c_reqs";
const CACHE_RESP_PREFIX: &'static str = "c_resp";
const CACHE_REQS_RESP_PREFIX: &'static str = "c_re";

pub(super) fn invalidate(cache: &impl Cache, pattern: &InvalidationPattern) {
let pattern_str = match pattern {
InvalidationPattern::FlushAll => String::from("*"),
InvalidationPattern::RequestsResponses(value) => {
format!("{}*{}*", CACHE_REQS_RESP_PREFIX, &value)
}
InvalidationPattern::Tokens => String::from(TOKENS_KEY),
};

cache.invalidate_pattern(pattern_str.as_str());
cache.invalidate_pattern(pattern.to_pattern_string().as_str());
}

pub(super) async fn cache_response<S>(
Expand Down
67 changes: 64 additions & 3 deletions src/cache/cache_operations.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::cache::cache_op_executors::{cache_response, invalidate, request_cached};
use crate::cache::Cache;
use crate::cache::{Cache, CACHE_REQS_PREFIX, CACHE_REQS_RESP_PREFIX, CACHE_RESP_PREFIX};
use crate::config::{
default_request_timeout, request_cache_duration, request_error_cache_duration,
};
use crate::providers::info::TOKENS_KEY;
use crate::utils::errors::ApiResult;
use rocket::futures::future::BoxFuture;
use rocket::futures::FutureExt;
use rocket::response::content;
use serde::Deserialize;
use serde::Serialize;
use std::future::Future;

Expand All @@ -20,12 +22,71 @@ pub struct Invalidate {
database: Database,
}

#[derive(Deserialize, Debug)]
pub enum InvalidationScope {
Requests,
Responses,
Both,
}

#[derive(Deserialize, Debug)]
#[serde(tag = "invalidate", content = "pattern_details")]
pub enum InvalidationPattern {
FlushAll,
RequestsResponses(String),
Any(InvalidationScope, String),
Transactions(InvalidationScope, String),
Balances(InvalidationScope, String),
Collectibles(InvalidationScope, String),
Transfers(InvalidationScope, String),
Contracts,
Tokens,
}

impl InvalidationPattern {
pub(super) fn to_pattern_string(&self) -> String {
match &self {
InvalidationPattern::Any(scope, value) => {
format!("{}*{}*", scope.invalidation_scope_string(), &value)
}
InvalidationPattern::Balances(scope, value) => {
format!("{}*/{}/balances*", scope.invalidation_scope_string(), value)
}
InvalidationPattern::Collectibles(scope, value) => {
format!(
"{}*/{}/collectibles*",
scope.invalidation_scope_string(),
value
)
}
InvalidationPattern::Transfers(scope, value) => {
format!(
"{}*/{}/*transfer*",
scope.invalidation_scope_string(),
value
)
}
InvalidationPattern::Transactions(scope, value) => {
format!(
"{}*/{}/*transactions/*",
scope.invalidation_scope_string(),
value
)
}
InvalidationPattern::Contracts => String::from("*contract*"),
InvalidationPattern::Tokens => String::from(TOKENS_KEY),
}
}
}

impl InvalidationScope {
pub(super) fn invalidation_scope_string(&self) -> &str {
match &self {
InvalidationScope::Requests => CACHE_REQS_PREFIX,
InvalidationScope::Responses => CACHE_RESP_PREFIX,
InvalidationScope::Both => CACHE_REQS_RESP_PREFIX,
}
}
}

impl Invalidate {
pub fn new(pattern: InvalidationPattern) -> Self {
Invalidate {
Expand Down
4 changes: 4 additions & 0 deletions src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ mod tests;

use mockall::automock;

const CACHE_REQS_PREFIX: &'static str = "c_reqs";
const CACHE_RESP_PREFIX: &'static str = "c_resp";
const CACHE_REQS_RESP_PREFIX: &'static str = "c_re";

#[automock]
pub trait Cache: Send + Sync {
fn fetch(&self, id: &str) -> Option<String>;
Expand Down
File renamed without changes.
102 changes: 102 additions & 0 deletions src/cache/tests/cache_operations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::cache::cache_operations::{InvalidationPattern, InvalidationScope};
use crate::cache::{CACHE_REQS_PREFIX, CACHE_REQS_RESP_PREFIX, CACHE_RESP_PREFIX};
use crate::providers::info::TOKENS_KEY;

#[test]
fn invalidation_pattern_any_string() {
let invalidation_pattern =
InvalidationPattern::Any(InvalidationScope::Both, "some_address".to_string());
let expected = format!("{}*some_address*", CACHE_REQS_RESP_PREFIX);

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_transactions_string() {
let invalidation_pattern =
InvalidationPattern::Transactions(InvalidationScope::Both, "some_address".to_string());
let expected = format!("{}*/some_address/*transactions/*", CACHE_REQS_RESP_PREFIX);

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_transfers_string() {
let invalidation_pattern =
InvalidationPattern::Transfers(InvalidationScope::Requests, "some_address".to_string());
let expected = format!("{}*/some_address/*transfer*", CACHE_REQS_PREFIX);

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_tokens_string() {
let invalidation_pattern = InvalidationPattern::Tokens;
let expected = TOKENS_KEY.to_string();

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_contracts_string() {
let invalidation_pattern = InvalidationPattern::Contracts;
let expected = String::from("*contract*");

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_balances_string() {
let invalidation_pattern =
InvalidationPattern::Balances(InvalidationScope::Both, "some_address".to_string());
let expected = format!("{}*/some_address/balances*", CACHE_REQS_RESP_PREFIX);

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_pattern_collectibles_string() {
let invalidation_pattern =
InvalidationPattern::Collectibles(InvalidationScope::Both, "some_address".to_string());
let expected = format!("{}*/some_address/collectibles*", CACHE_REQS_RESP_PREFIX);

let actual = invalidation_pattern.to_pattern_string();

assert_eq!(expected, actual);
}

#[test]
fn invalidation_scope_both_to_string() {
assert_eq!(
CACHE_REQS_RESP_PREFIX,
InvalidationScope::Both.invalidation_scope_string()
)
}

#[test]
fn invalidation_scope_requests_to_string() {
assert_eq!(
CACHE_REQS_PREFIX,
InvalidationScope::Requests.invalidation_scope_string()
)
}

#[test]
fn invalidation_scope_responses_to_string() {
assert_eq!(
CACHE_RESP_PREFIX,
InvalidationScope::Responses.invalidation_scope_string()
)
}
3 changes: 2 additions & 1 deletion src/cache/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod cache;
mod cache_inner;
mod cache_operations;
20 changes: 7 additions & 13 deletions src/routes/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::utils::context::Context;
use crate::utils::errors::ApiResult;
use rocket_contrib::json::Json;

#[doc(hidden)]
#[post("/v1/hook/update/<token>", format = "json", data = "<update>")]
pub fn update(context: Context<'_>, token: String, update: Json<Payload>) -> ApiResult<()> {
if token != webhook_token() {
Expand All @@ -15,20 +14,15 @@ pub fn update(context: Context<'_>, token: String, update: Json<Payload>) -> Api
invalidate_caches(context.cache(), &update)
}

#[get("/v1/flush_all/<token>")]
pub fn flush_all(context: Context<'_>, token: String) -> ApiResult<()> {
#[post("/v1/flush/<token>", format = "json", data = "<invalidation_pattern>")]
pub fn flush(
context: Context,
token: String,
invalidation_pattern: Json<InvalidationPattern>,
) -> ApiResult<()> {
if token != webhook_token() {
bail!("Invalid token");
}
Invalidate::new(InvalidationPattern::FlushAll).execute(context.cache());
Ok(())
}

#[get("/v1/flush_tokens/<token>")]
pub fn flush_token_info(context: Context, token: String) -> ApiResult<()> {
if token != webhook_token() {
bail!("Invalid token");
}
Invalidate::new(InvalidationPattern::Tokens).execute(context.cache());
Invalidate::new(invalidation_pattern.0).execute(context.cache());
Ok(())
}
3 changes: 1 addition & 2 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ pub fn active_routes() -> Vec<Route> {
transactions::submit_confirmation,
transactions::propose_transaction,
hooks::update,
hooks::flush_all,
hooks::flush_token_info,
hooks::flush,
health::health
]
}
Expand Down
26 changes: 15 additions & 11 deletions src/services/hooks.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
use crate::cache::cache_operations::{Invalidate, InvalidationPattern};
use crate::cache::cache_operations::{Invalidate, InvalidationPattern, InvalidationScope};
use crate::cache::Cache;
use crate::models::backend::webhooks::{Payload, PayloadDetails};
use crate::utils::errors::ApiResult;

pub fn invalidate_caches(cache: &impl Cache, payload: &Payload) -> ApiResult<()> {
Invalidate::new(InvalidationPattern::RequestsResponses(
Invalidate::new(InvalidationPattern::Any(
InvalidationScope::Both,
payload.address.to_owned(),
))
.execute(cache);
payload.details.as_ref().map(|d| match d {
PayloadDetails::NewConfirmation(data) => {
Invalidate::new(InvalidationPattern::RequestsResponses(String::from(
&data.safe_tx_hash,
)))
Invalidate::new(InvalidationPattern::Any(
InvalidationScope::Both,
String::from(&data.safe_tx_hash),
))
.execute(cache);
}
PayloadDetails::ExecutedMultisigTransaction(data) => {
Invalidate::new(InvalidationPattern::RequestsResponses(String::from(
&data.safe_tx_hash,
)))
Invalidate::new(InvalidationPattern::Any(
InvalidationScope::Both,
String::from(&data.safe_tx_hash),
))
.execute(cache);
}
PayloadDetails::PendingMultisigTransaction(data) => {
Invalidate::new(InvalidationPattern::RequestsResponses(String::from(
&data.safe_tx_hash,
)))
Invalidate::new(InvalidationPattern::Any(
InvalidationScope::Both,
String::from(&data.safe_tx_hash),
))
.execute(cache);
}
_ => {}
Expand Down

0 comments on commit 40296f7

Please sign in to comment.