Skip to content

Commit

Permalink
Merge branch 'main' into feat/1564-project-duration
Browse files Browse the repository at this point in the history
  • Loading branch information
LynxLynxx authored Jan 29, 2025
2 parents c0c595a + ff767f6 commit 1612dee
Show file tree
Hide file tree
Showing 29 changed files with 827 additions and 231 deletions.
3 changes: 1 addition & 2 deletions catalyst-gateway/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ workspace = true
cardano-chain-follower = {version = "0.0.6", git = "https://github.com/input-output-hk/catalyst-libs.git", tag="v0.0.10" }
c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "v0.0.3" }
rbac-registration = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "v0.0.8" }
catalyst-signed-doc = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250123-00" }

catalyst-signed-doc = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250128-00" }

pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
pallas-traverse = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
Expand Down
32 changes: 32 additions & 0 deletions catalyst-gateway/bin/src/db/event/signed_docs/doc_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Document Reference filtering object.
use crate::db::event::common::eq_or_ranged_uuid::EqOrRangedUuid;

/// Document Reference filtering struct.
#[derive(Clone, Debug)]
pub(crate) struct DocumentRef {
/// Document id filtering
pub(crate) id: Option<EqOrRangedUuid>,
/// Document ver filtering
pub(crate) ver: Option<EqOrRangedUuid>,
}

impl DocumentRef {
/// Return a sql conditional statement by the provided `table_field`
pub(crate) fn conditional_stmt(&self, table_field: &str) -> String {
let mut stmt = "TRUE".to_string();
if let Some(id) = &self.id {
stmt.push_str(&format!(
" AND {}",
id.conditional_stmt(&format!("({table_field}->>'id')::uuid"))
));
}
if let Some(ver) = &self.ver {
stmt.push_str(&format!(
" AND {}",
ver.conditional_stmt(&format!("({table_field}->>'ver')::uuid"))
));
}
stmt
}
}
3 changes: 2 additions & 1 deletion catalyst-gateway/bin/src/db/event/signed_docs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! Signed docs queries
mod doc_ref;
mod full_signed_doc;
mod query_filter;
mod signed_doc_body;
#[cfg(test)]
mod tests;

#[allow(unused_imports)]
pub(crate) use doc_ref::DocumentRef;
pub(crate) use full_signed_doc::{FullSignedDoc, SELECT_SIGNED_DOCS_TEMPLATE};
pub(crate) use query_filter::DocsQueryFilter;
pub(crate) use signed_doc_body::{SignedDocBody, FILTERED_SELECT_SIGNED_DOCS_TEMPLATE};
114 changes: 99 additions & 15 deletions catalyst-gateway/bin/src/db/event/signed_docs/query_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@
use std::fmt::Display;

use super::DocumentRef;
use crate::db::event::common::eq_or_ranged_uuid::EqOrRangedUuid;

/// A `select_signed_docs` query filtering argument.
/// If all fields would be `None` the query will search for all entries from the db.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub(crate) struct DocsQueryFilter {
/// `type` field
doc_type: Option<uuid::Uuid>,
/// `id` field
id: Option<EqOrRangedUuid>,
/// `ver` field
ver: Option<EqOrRangedUuid>,
/// `metadata` field
metadata: Option<serde_json::Value>,
/// `metadata->'ref'` field
doc_ref: Option<DocumentRef>,
/// `metadata->'template'` field
template: Option<DocumentRef>,
/// `metadata->'reply'` field
reply: Option<DocumentRef>,
/// `metadata->'brand_id'` field
brand_id: Option<DocumentRef>,
/// `metadata->'campaign_id'` field
campaign_id: Option<DocumentRef>,
/// `metadata->'category_id'` field
category_id: Option<DocumentRef>,
}

impl Display for DocsQueryFilter {
Expand All @@ -37,8 +48,47 @@ impl Display for DocsQueryFilter {
ver.conditional_stmt("signed_docs.ver")
)?;
}
if let Some(metadata) = &self.metadata {
write!(&mut query, " AND signed_docs.metadata @> '{metadata}'",)?;
if let Some(doc_ref) = &self.doc_ref {
write!(
&mut query,
" AND {}",
doc_ref.conditional_stmt("metadata->'ref'")
)?;
}
if let Some(template) = &self.template {
write!(
&mut query,
" AND {}",
template.conditional_stmt("metadata->'template'")
)?;
}
if let Some(reply) = &self.reply {
write!(
&mut query,
" AND {}",
reply.conditional_stmt("metadata->'reply'")
)?;
}
if let Some(brand_id) = &self.brand_id {
write!(
&mut query,
" AND {}",
brand_id.conditional_stmt("metadata->'brand_id'")
)?;
}
if let Some(campaign_id) = &self.campaign_id {
write!(
&mut query,
" AND {}",
campaign_id.conditional_stmt("metadata->'campaign_id'")
)?;
}
if let Some(category_id) = &self.category_id {
write!(
&mut query,
" AND {}",
category_id.conditional_stmt("metadata->'category_id'")
)?;
}

write!(f, "{query}")
Expand All @@ -48,12 +98,7 @@ impl Display for DocsQueryFilter {
impl DocsQueryFilter {
/// Creates an empty filter stmt, so the query will retrieve all entries from the db.
pub fn all() -> Self {
DocsQueryFilter {
doc_type: None,
id: None,
ver: None,
metadata: None,
}
DocsQueryFilter::default()
}

/// Set the `type` field filter condition
Expand All @@ -80,11 +125,50 @@ impl DocsQueryFilter {
}
}

/// Set the `metadata` field filter condition
#[allow(dead_code)]
pub fn with_metadata(self, metadata: serde_json::Value) -> Self {
/// Set the `metadata->'ref'` field filter condition
pub fn with_ref(self, doc_ref: DocumentRef) -> Self {
DocsQueryFilter {
doc_ref: Some(doc_ref),
..self
}
}

/// Set the `metadata->'template'` field filter condition
pub fn with_template(self, template: DocumentRef) -> Self {
DocsQueryFilter {
template: Some(template),
..self
}
}

/// Set the `metadata->'reply'` field filter condition
pub fn with_reply(self, reply: DocumentRef) -> Self {
DocsQueryFilter {
reply: Some(reply),
..self
}
}

/// Set the `metadata->'brand_id'` field filter condition
pub fn with_brand_id(self, brand_id: DocumentRef) -> Self {
DocsQueryFilter {
brand_id: Some(brand_id),
..self
}
}

/// Set the `metadata->'campaign_id'` field filter condition
pub fn with_campaign_id(self, campaign_id: DocumentRef) -> Self {
DocsQueryFilter {
campaign_id: Some(campaign_id),
..self
}
}

/// Set the `metadata->'category_id'` field filter condition
pub fn with_category_id(self, category_id: DocumentRef) -> Self {
DocsQueryFilter {
metadata: Some(metadata),
category_id: Some(category_id),
..self
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! `filter_by_field` macro
/// `filter_by_field` test case
#[macro_export]
macro_rules! filter_by_field {
($doc:expr, $field:expr, $with_method:ident) => {
#[allow(clippy::indexing_slicing)]
if let Some(meta) = $doc.metadata() {
let id = uuid::Uuid::from_str(meta[$field]["id"].clone().as_str().unwrap()).unwrap();
let ver = uuid::Uuid::from_str(meta[$field]["ver"].clone().as_str().unwrap()).unwrap();

// With id
let filter = DocsQueryFilter::all().$with_method(DocumentRef {
id: Some(EqOrRangedUuid::Eq(id)),
ver: None,
});
let mut res_docs = SignedDocBody::retrieve(&filter, &QueryLimits::ALL)
.await
.unwrap();
let res_doc = res_docs.try_next().await.unwrap().unwrap();
assert_eq!($doc.body(), &res_doc);

// With ver
let filter = DocsQueryFilter::all().$with_method(DocumentRef {
id: None,
ver: Some(EqOrRangedUuid::Eq(ver)),
});
let mut res_docs = SignedDocBody::retrieve(&filter, &QueryLimits::ALL)
.await
.unwrap();
let res_doc = res_docs.try_next().await.unwrap().unwrap();
assert_eq!($doc.body(), &res_doc);

// With both id and ver
let filter = DocsQueryFilter::all().$with_method(DocumentRef {
id: Some(EqOrRangedUuid::Eq(id)),
ver: Some(EqOrRangedUuid::Eq(ver)),
});
let mut res_docs = SignedDocBody::retrieve(&filter, &QueryLimits::ALL)
.await
.unwrap();
let res_doc = res_docs.try_next().await.unwrap().unwrap();
assert_eq!($doc.body(), &res_doc);
}
};
}

pub(super) use filter_by_field;
79 changes: 43 additions & 36 deletions catalyst-gateway/bin/src/db/event/signed_docs/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Integration tests of the `signed docs` queries
use std::str::FromStr;

use futures::TryStreamExt;

use super::*;
Expand All @@ -8,6 +10,35 @@ use crate::db::event::{
establish_connection,
};

mod filter_by_field;

#[ignore = "An integration test which requires a running EventDB instance, disabled from `testunit` CI run"]
#[tokio::test]
async fn queries_test() {
establish_connection();

let doc_type = uuid::Uuid::new_v4();
let docs = test_docs(doc_type);

for doc in &docs {
store_full_signed_doc(doc, doc_type).await;
retrieve_full_signed_doc(doc).await;
filter_by_id(doc).await;
filter_by_ver(doc).await;
filter_by_id_and_ver(doc).await;

filter_by_field::filter_by_field!(doc, "ref", with_ref);
filter_by_field::filter_by_field!(doc, "template", with_template);
filter_by_field::filter_by_field!(doc, "reply", with_reply);
filter_by_field::filter_by_field!(doc, "brand_id", with_brand_id);
filter_by_field::filter_by_field!(doc, "campaign_id", with_campaign_id);
filter_by_field::filter_by_field!(doc, "category_id", with_category_id);
}

filter_by_type(&docs, doc_type).await;
filter_all(&docs).await;
}

fn test_docs(doc_type: uuid::Uuid) -> Vec<FullSignedDoc> {
vec![
FullSignedDoc::new(
Expand All @@ -18,8 +49,12 @@ fn test_docs(doc_type: uuid::Uuid) -> Vec<FullSignedDoc> {
vec!["Alex".to_string()],
Some(serde_json::json!(
{
"name": "Alex",
"amount": 105,
"ref": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"template": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"reply": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"brand_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"campaign_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"category_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
}
)),
),
Expand All @@ -34,8 +69,12 @@ fn test_docs(doc_type: uuid::Uuid) -> Vec<FullSignedDoc> {
vec!["Steven".to_string()],
Some(serde_json::json!(
{
"name": "Steven",
"amount": 15,
"ref": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"template": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"reply": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"brand_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"campaign_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
"category_id": { "id": uuid::Uuid::now_v7(), "ver": uuid::Uuid::now_v7() },
}
)),
),
Expand Down Expand Up @@ -138,17 +177,6 @@ async fn filter_by_id_and_ver(doc: &FullSignedDoc) {
assert!(res_docs.try_next().await.unwrap().is_none());
}

async fn filter_by_metadata(doc: &FullSignedDoc) {
if let Some(meta) = doc.metadata() {
let filter = DocsQueryFilter::all().with_metadata(meta.clone());
let mut res_docs = SignedDocBody::retrieve(&filter, &QueryLimits::ALL)
.await
.unwrap();
let res_doc = res_docs.try_next().await.unwrap().unwrap();
assert_eq!(doc.body(), &res_doc);
}
}

async fn filter_by_type(docs: &[FullSignedDoc], doc_type: uuid::Uuid) {
let filter = DocsQueryFilter::all().with_type(doc_type);
let mut res_docs = SignedDocBody::retrieve(&filter, &QueryLimits::ALL)
Expand All @@ -170,24 +198,3 @@ async fn filter_all(docs: &[FullSignedDoc]) {
assert_eq!(exp_doc.body(), &res_doc);
}
}

#[ignore = "An integration test which requires a running EventDB instance, disabled from `testunit` CI run"]
#[tokio::test]
async fn queries_test() {
establish_connection();

let doc_type = uuid::Uuid::new_v4();
let docs = test_docs(doc_type);

for doc in &docs {
store_full_signed_doc(doc, doc_type).await;
retrieve_full_signed_doc(doc).await;
filter_by_id(doc).await;
filter_by_ver(doc).await;
filter_by_id_and_ver(doc).await;
filter_by_metadata(doc).await;
}

filter_by_type(&docs, doc_type).await;
filter_all(&docs).await;
}
1 change: 1 addition & 0 deletions catalyst-gateway/bin/src/service/api/documents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl DocumentApi {
Err(_) => {
put_document::Responses::BadRequest(Json(PutDocumentBadRequest::new(
"Failed to read document from the request",
None,
)))
.into()
},
Expand Down
Loading

0 comments on commit 1612dee

Please sign in to comment.