Skip to content

Commit b7bbe15

Browse files
committed
finilize validation logic
1 parent 40c82d2 commit b7bbe15

File tree

8 files changed

+279
-129
lines changed

8 files changed

+279
-129
lines changed

rust/signed_doc/src/validator/mod.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
//! Catalyst Signed Documents validation
22
3-
#![allow(dead_code)]
4-
53
pub(crate) mod rules;
64
pub(crate) mod utils;
75

86
use std::{collections::HashMap, sync::LazyLock};
97

108
use catalyst_types::uuid::Uuid;
11-
use rules::{ContentEncodingRule, ContentTypeRule, Rules};
9+
use rules::{
10+
CategoryRule, ContentEncodingRule, ContentTypeRule, RefRule, ReplyRule, Rules, SectionRule,
11+
TemplateRule,
12+
};
1213

1314
use crate::{
14-
doc_types::{COMMENT_DOCUMENT_UUID_TYPE, PROPOSAL_DOCUMENT_UUID_TYPE},
15+
doc_types::{
16+
COMMENT_DOCUMENT_UUID_TYPE, COMMENT_TEMPLATE_UUID_TYPE, PROPOSAL_DOCUMENT_UUID_TYPE,
17+
PROPOSAL_TEMPLATE_UUID_TYPE,
18+
},
1519
providers::CatalystSignedDocumentProvider,
1620
CatalystSignedDocument, ContentEncoding, ContentType,
1721
};
@@ -22,7 +26,7 @@ static DOCUMENT_RULES: LazyLock<HashMap<Uuid, Rules>> = LazyLock::new(document_r
2226
/// `DOCUMENT_RULES` initialization function
2327
fn document_rules_init() -> HashMap<Uuid, Rules> {
2428
let mut document_rules_map = HashMap::new();
25-
29+
2630
let proposal_document_rules = Rules {
2731
content_type: ContentTypeRule {
2832
exp: ContentType::Json,
@@ -31,6 +35,13 @@ fn document_rules_init() -> HashMap<Uuid, Rules> {
3135
exp: ContentEncoding::Brotli,
3236
optional: false,
3337
},
38+
template: TemplateRule::Specified {
39+
exp_template_type: PROPOSAL_TEMPLATE_UUID_TYPE,
40+
},
41+
category: CategoryRule::Specified { optional: false },
42+
doc_ref: RefRule::NotSpecified,
43+
reply: ReplyRule::NotSpecified,
44+
section: SectionRule::NotSpecified,
3445
};
3546
document_rules_map.insert(PROPOSAL_DOCUMENT_UUID_TYPE, proposal_document_rules);
3647

@@ -42,6 +53,19 @@ fn document_rules_init() -> HashMap<Uuid, Rules> {
4253
exp: ContentEncoding::Brotli,
4354
optional: false,
4455
},
56+
template: TemplateRule::Specified {
57+
exp_template_type: COMMENT_TEMPLATE_UUID_TYPE,
58+
},
59+
doc_ref: RefRule::Specified {
60+
exp_ref_type: PROPOSAL_DOCUMENT_UUID_TYPE,
61+
optional: false,
62+
},
63+
reply: ReplyRule::Specified {
64+
exp_reply_type: COMMENT_DOCUMENT_UUID_TYPE,
65+
optional: true,
66+
},
67+
section: SectionRule::Specified { optional: true },
68+
category: CategoryRule::NotSpecified,
4569
};
4670
document_rules_map.insert(COMMENT_DOCUMENT_UUID_TYPE, comment_document_rules);
4771

rust/signed_doc/src/validator/rules/category.rs

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ use crate::{
66
};
77

88
/// `category_id` field validation rule
9-
pub(crate) struct CategoryRule {
10-
/// optional flag for the `category_id` field
11-
pub(crate) optional: bool,
9+
#[derive(Clone, Debug, PartialEq)]
10+
pub(crate) enum CategoryRule {
11+
/// Is 'category_id' specified
12+
Specified {
13+
/// optional flag for the `category_id` field
14+
optional: bool,
15+
},
16+
/// 'category_id' is not specified
17+
NotSpecified,
1218
}
1319

1420
impl CategoryRule {
@@ -17,26 +23,39 @@ impl CategoryRule {
1723
&self, doc: &CatalystSignedDocument, provider: &Provider,
1824
) -> anyhow::Result<bool>
1925
where Provider: 'static + CatalystSignedDocumentProvider {
20-
if let Some(category) = &doc.doc_meta().category_id() {
21-
let category_validator = |category_doc: CatalystSignedDocument| {
22-
if category_doc.doc_type()?.uuid() != CATEGORY_DOCUMENT_UUID_TYPE {
23-
doc.report().invalid_value(
24-
"category_id",
25-
category_doc.doc_type()?.to_string().as_str(),
26-
CATEGORY_DOCUMENT_UUID_TYPE.to_string().as_str(),
27-
"Invalid referenced category document type",
28-
);
29-
return Ok(false);
30-
}
31-
Ok(true)
32-
};
33-
return validate_provided_doc(category, provider, doc.report(), category_validator)
34-
.await;
35-
} else if !self.optional {
36-
doc.report()
37-
.missing_field("category_id", "Document must have a category field");
38-
return Ok(false);
26+
if let Self::Specified { optional } = self {
27+
if let Some(category) = &doc.doc_meta().category_id() {
28+
let category_validator = |category_doc: CatalystSignedDocument| {
29+
if category_doc.doc_type()?.uuid() != CATEGORY_DOCUMENT_UUID_TYPE {
30+
doc.report().invalid_value(
31+
"category_id",
32+
category_doc.doc_type()?.to_string().as_str(),
33+
CATEGORY_DOCUMENT_UUID_TYPE.to_string().as_str(),
34+
"Invalid referenced category document type",
35+
);
36+
return Ok(false);
37+
}
38+
Ok(true)
39+
};
40+
return validate_provided_doc(category, provider, doc.report(), category_validator)
41+
.await;
42+
} else if !optional {
43+
doc.report()
44+
.missing_field("category_id", "Document must have a category field");
45+
return Ok(false);
46+
}
3947
}
48+
if &Self::NotSpecified == self {
49+
if let Some(category) = doc.doc_meta().category_id() {
50+
doc.report().unknown_field(
51+
"category_id",
52+
&category.to_string(),
53+
"Document does not expect to have a category field",
54+
);
55+
return Ok(false);
56+
}
57+
}
58+
4059
Ok(true)
4160
}
4261
}

rust/signed_doc/src/validator/rules/content_type.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::{metadata::ContentType, CatalystSignedDocument};
44

55
/// `content-type` field validation rule
6+
#[derive(Clone, Debug, PartialEq)]
67
pub(crate) struct ContentTypeRule {
78
/// expected `content-type` field
89
pub(crate) exp: ContentType,
@@ -24,8 +25,3 @@ impl ContentTypeRule {
2425
Ok(true)
2526
}
2627
}
27-
28-
#[cfg(test)]
29-
mod tests {
30-
31-
}

rust/signed_doc/src/validator/rules/doc_ref.rs

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,61 @@ use crate::{
88
};
99

1010
/// `ref` field validation rule
11-
pub(crate) struct RefRule {
12-
/// expected `type` field of the referenced doc
13-
pub(crate) ref_type: Uuid,
14-
/// optional flag for the `ref` field
15-
pub(crate) optional: bool,
11+
#[derive(Clone, Debug, PartialEq)]
12+
pub(crate) enum RefRule {
13+
/// Is 'ref' specified
14+
Specified {
15+
/// expected `type` field of the referenced doc
16+
exp_ref_type: Uuid,
17+
/// optional flag for the `ref` field
18+
optional: bool,
19+
},
20+
/// 'ref' is not specified
21+
NotSpecified,
1622
}
1723
impl RefRule {
1824
/// Field validation rule
1925
pub(crate) async fn check<Provider>(
2026
&self, doc: &CatalystSignedDocument, provider: &Provider,
2127
) -> anyhow::Result<bool>
2228
where Provider: 'static + CatalystSignedDocumentProvider {
23-
if let Some(doc_ref) = doc.doc_meta().doc_ref() {
24-
let ref_validator = |proposal_doc: CatalystSignedDocument| {
25-
if proposal_doc.doc_type()?.uuid() != self.ref_type {
26-
doc.report().invalid_value(
27-
"ref",
28-
proposal_doc.doc_type()?.to_string().as_str(),
29-
self.ref_type.to_string().as_str(),
30-
"Invalid referenced proposal document type",
31-
);
32-
return Ok(false);
33-
}
34-
Ok(true)
35-
};
36-
return validate_provided_doc(&doc_ref, provider, doc.report(), ref_validator).await;
37-
} else if !self.optional {
38-
doc.report()
39-
.missing_field("ref", "Document must have a ref field");
40-
return Ok(false);
29+
if let Self::Specified {
30+
exp_ref_type,
31+
optional,
32+
} = self
33+
{
34+
if let Some(doc_ref) = doc.doc_meta().doc_ref() {
35+
let ref_validator = |ref_doc: CatalystSignedDocument| {
36+
if &ref_doc.doc_type()?.uuid() != exp_ref_type {
37+
doc.report().invalid_value(
38+
"ref",
39+
ref_doc.doc_type()?.to_string().as_str(),
40+
exp_ref_type.to_string().as_str(),
41+
"Invalid referenced document type",
42+
);
43+
return Ok(false);
44+
}
45+
Ok(true)
46+
};
47+
return validate_provided_doc(&doc_ref, provider, doc.report(), ref_validator)
48+
.await;
49+
} else if !optional {
50+
doc.report()
51+
.missing_field("ref", "Document must have a ref field");
52+
return Ok(false);
53+
}
4154
}
55+
if &Self::NotSpecified == self {
56+
if let Some(doc_ref) = doc.doc_meta().doc_ref() {
57+
doc.report().unknown_field(
58+
"ref",
59+
&doc_ref.to_string(),
60+
"Document does not expect to have a ref field",
61+
);
62+
return Ok(false);
63+
}
64+
}
65+
4266
Ok(true)
4367
}
4468
}

rust/signed_doc/src/validator/rules/mod.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! A list of validation rules for all metadata fields
22
//! <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/>
33
4-
use futures::try_join;
4+
use futures::FutureExt;
55

66
use crate::{providers::CatalystSignedDocumentProvider, CatalystSignedDocument};
77

@@ -27,20 +27,39 @@ pub(crate) struct Rules {
2727
pub(crate) content_type: ContentTypeRule,
2828
/// 'content-encoding' field validation rule
2929
pub(crate) content_encoding: ContentEncodingRule,
30+
/// 'ref' field validation tule
31+
pub(crate) doc_ref: RefRule,
32+
/// 'template' field validation rule
33+
pub(crate) template: TemplateRule,
34+
/// 'reply' field validation rule
35+
pub(crate) reply: ReplyRule,
36+
/// 'section' field validation rule
37+
pub(crate) section: SectionRule,
38+
/// 'category' field validation rule
39+
pub(crate) category: CategoryRule,
3040
}
3141

3242
impl Rules {
3343
pub(crate) async fn check<Provider>(
34-
&self, doc: &CatalystSignedDocument, _provider: &Provider,
44+
&self, doc: &CatalystSignedDocument, provider: &Provider,
3545
) -> anyhow::Result<bool>
3646
where Provider: 'static + CatalystSignedDocumentProvider {
37-
if let (true, true) = try_join!(
38-
self.content_type.check(doc),
39-
self.content_encoding.check(doc)
40-
)? {
41-
Ok(true)
42-
} else {
43-
Ok(false)
44-
}
47+
let rules = [
48+
self.content_type.check(doc).boxed(),
49+
self.content_encoding.check(doc).boxed(),
50+
self.doc_ref.check(doc, provider).boxed(),
51+
self.template.check(doc, provider).boxed(),
52+
self.reply.check(doc, provider).boxed(),
53+
self.section.check(doc).boxed(),
54+
self.category.check(doc, provider).boxed(),
55+
];
56+
57+
let res = futures::future::join_all(rules)
58+
.await
59+
.into_iter()
60+
.collect::<anyhow::Result<Vec<_>>>()?
61+
.iter()
62+
.all(|res| *res);
63+
Ok(res)
4564
}
4665
}

0 commit comments

Comments
 (0)