Skip to content

Commit

Permalink
[fix] #1804: fix schema generation for HashOf, SignatureOf, add test …
Browse files Browse the repository at this point in the history
…to ensure no schemas are missing

Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
  • Loading branch information
mversic authored Jan 12, 2022
1 parent 84d01ac commit 79d0df6
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 98 deletions.
18 changes: 15 additions & 3 deletions crypto/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use core::{
};

use derive_more::{Deref, DerefMut, Display};
use iroha_schema::IntoSchema;
use iroha_schema::prelude::*;
use parity_scale_codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
Expand Down Expand Up @@ -157,8 +157,20 @@ impl<T: Encode> HashOf<T> {
}

impl<T> IntoSchema for HashOf<T> {
fn schema(metamap: &mut iroha_schema::MetaMap) {
Hash::schema(metamap)
fn schema(map: &mut MetaMap) {
let type_name = Self::type_name();

Hash::schema(map);
if !map.contains_key(&type_name) {
// Field was just inserted above
#[allow(clippy::expect_used)]
let wrapped_type_metadata = map
.get(&Hash::type_name())
.expect("Wrapped type metadata should have been present in the schemas")
.clone();

map.insert(type_name, wrapped_type_metadata);
}
}
}

Expand Down
18 changes: 15 additions & 3 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::{fmt, marker::PhantomData};
use std::collections::{btree_map, btree_set};

use derive_more::{Deref, DerefMut};
use iroha_schema::IntoSchema;
use iroha_schema::prelude::*;
use parity_scale_codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
Expand Down Expand Up @@ -150,8 +150,20 @@ impl<T> Ord for SignatureOf<T> {
}

impl<T> IntoSchema for SignatureOf<T> {
fn schema(metamap: &mut iroha_schema::MetaMap) {
Signature::schema(metamap)
fn schema(map: &mut MetaMap) {
let type_name = Self::type_name();

Signature::schema(map);
if !map.contains_key(&type_name) {
// Field was just inserted above
#[allow(clippy::expect_used)]
let wrapped_type_metadata = map
.get(&Signature::type_name())
.expect("Wrapped type metadata should have been present in the schemas")
.clone();

map.insert(type_name, wrapped_type_metadata);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions data_model/src/expression.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Expressions to use inside of ISIs.

#![allow(
// Because of `serde(skip)`
// Because of `codec(skip)`
clippy::default_trait_access,
// Because of length on instructions and expressions (can't be 0)
clippy::len_without_is_empty,
Expand Down Expand Up @@ -33,11 +33,11 @@ pub type ExpressionBox = Box<Expression>;

/// Struct for type checking and converting expression results.
#[derive(Debug, Clone, Encode, Decode, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(transparent)]
pub struct EvaluatesTo<V: TryFrom<Value>> {
/// Expression.
#[serde(flatten)]
pub expression: ExpressionBox,
#[serde(skip)]
#[codec(skip)]
_value_type: PhantomData<V>,
}
Expand Down
7 changes: 0 additions & 7 deletions data_model/src/isi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,6 @@ pub struct RemoveKeyValueBox {
pub key: EvaluatesTo<Name>,
}

/// Sized structure for all possible Sets.
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, IntoSchema)]
pub struct SetBox {
/// Object to set as a value.
pub object: EvaluatesTo<Value>,
}

/// Sized structure for all possible Registers.
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, IntoSchema)]
pub struct RegisterBox {
Expand Down
144 changes: 76 additions & 68 deletions schema/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,23 @@

#![allow(clippy::print_stdout)]

use std::collections::BTreeMap;

use iroha_core::block::stream::prelude::*;
use iroha_schema::prelude::*;

macro_rules! to_json {
($($t:ty),* $(,)?) => {{
let mut out = BTreeMap::new();
$(<$t as IntoSchema>::schema(&mut out);)*
serde_json::to_string_pretty(&out).unwrap()
}};
}

fn main() {
fn build_schemas() -> MetaMap {
use iroha_core::genesis::RawGenesisBlock;
use iroha_data_model::{
expression::*,
isi::{If, *},
prelude::*,
};
use iroha_data_model::prelude::*;

let json = to_json! {
// $ rg '^pub (struct|enum)' | rg -v '(<|Builder|LengthLimits|QueryRequest)' | cut -d' ' -f3 | sed -e 's/[(].*//' -e 's/$/,/' | sort
Add,
And,
BlockPublisherMessage,
BlockSubscriberMessage,
BurnBox,
Contains,
ContainsAll,
ContainsAny,
ContextValue,
Divide,
Equal,
Event,
EventFilter,
EventPublisherMessage,
EventSubscriberMessage,
Expression,
FailBox,
GrantBox,
Greater,
IdBox,
IdentifiableBox,
If,
If,
Instruction,
Less,
MintBox,
Mod,
Multiply,
Not,
Or,
Pair,
Parameter,
Payload,
QueryBox,
QueryResult,
RaiseTo,
RegisterBox,
RemoveKeyValueBox,
SequenceBox,
SetBox,
SetKeyValueBox,
SignedQueryRequest,
Subtract,
TransferBox,
UnregisterBox,
Value,
Where,
macro_rules! schemas {
($($t:ty),* $(,)?) => {{
let mut out = MetaMap::new();
$(<$t as IntoSchema>::schema(&mut out);)*
out
}};
}

// All versioned
schemas! {
// It is sufficient to list top level types only
VersionedBlockPublisherMessage,
VersionedBlockSubscriberMessage,
VersionedEventPublisherMessage,
Expand All @@ -83,7 +28,70 @@ fn main() {
VersionedTransaction,

RawGenesisBlock
};
}
}

// Schemas should always be serializable to JSON
#[allow(clippy::expect_used)]
fn main() {
let schemas = build_schemas();

println!(
"{}",
serde_json::to_string_pretty(&schemas).expect("Unable to serialize schemas")
);
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use super::*;

fn find_missing_schemas(schemas: &MetaMap) -> HashMap<&str, Vec<&str>> {
let mut missing_schemas = HashMap::new();

for (type_name, schema) in schemas {
let types: Vec<&str> = match schema {
Metadata::Enum(EnumMeta { variants }) => variants
.iter()
.map(|v| &v.ty)
.filter_map(Option::as_ref)
.map(String::as_str)
.collect(),
Metadata::Struct(NamedFieldsMeta { declarations }) => {
declarations.iter().map(|d| d.ty.as_str()).collect()
}
Metadata::TupleStruct(UnnamedFieldsMeta { types }) => {
types.iter().map(String::as_str).collect()
}
Metadata::Result(ResultMeta { ok, err }) => vec![ok, err],
Metadata::Map(MapMeta { key, value }) => vec![key, value],
Metadata::Option(ty)
| Metadata::Array(ArrayMeta { ty, .. })
| Metadata::Vec(ty) => {
vec![ty]
}
Metadata::String | Metadata::Bool | Metadata::FixedPoint(_) | Metadata::Int(_) => {
vec![]
}
};

for ty in types {
if !schemas.contains_key(ty) {
missing_schemas
.entry(type_name.as_str())
.or_insert_with(Vec::new)
.push(ty);
}
}
}

missing_schemas
}

println!("{}", json)
#[test]
fn no_missing_schemas() {
assert!(find_missing_schemas(&build_schemas()).is_empty());
}
}
36 changes: 21 additions & 15 deletions schema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub trait DecimalPlacesAware {
}

/// Metadata
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub enum Metadata {
/// Structure with named fields
Struct(NamedFieldsMeta),
Expand Down Expand Up @@ -80,22 +80,24 @@ pub enum Metadata {
}

/// Array metadata
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct ArrayMeta {
ty: String,
len: usize,
/// Type
pub ty: String,
/// Length
pub len: usize,
}

/// Named fields
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct NamedFieldsMeta {
/// Fields
pub declarations: Vec<Declaration>,
//todo add collection of properties meta defined in struct
}

/// Field
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct Declaration {
/// Field name
pub name: String,
Expand All @@ -104,22 +106,22 @@ pub struct Declaration {
}

/// Unnamed fileds
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct UnnamedFieldsMeta {
/// Field types
pub types: Vec<String>,
//todo add collection of properties meta defined in struct
}

/// Enum metadata
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct EnumMeta {
/// Enum variants
pub variants: Vec<EnumVariant>,
}

/// Enum variant
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct EnumVariant {
/// Enum variant name
pub name: String,
Expand All @@ -131,7 +133,7 @@ pub struct EnumVariant {
}

/// Result variant
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct ResultMeta {
/// Ok type
pub ok: String,
Expand All @@ -140,7 +142,7 @@ pub struct ResultMeta {
}

/// Map variant
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct MapMeta {
/// Key type
pub key: String,
Expand All @@ -149,7 +151,7 @@ pub struct MapMeta {
}

/// Integer mode
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub enum IntMode {
/// Fixed width
FixedWidth,
Expand All @@ -158,11 +160,11 @@ pub enum IntMode {
}

/// Compact predicate. Just for documentation purposes
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct Compact<T>(T);

/// Fixed metadata
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct FixedMeta {
base: String,
decimal_places: usize,
Expand Down Expand Up @@ -319,7 +321,11 @@ impl<V: IntoSchema> IntoSchema for BTreeSet<V> {
format!("BTreeSet<{}>", V::type_name())
}
fn schema(map: &mut MetaMap) {
Vec::<V>::schema(map)
map.entry(Self::type_name())
.or_insert_with(|| Metadata::Vec(V::type_name()));
if !map.contains_key(&V::type_name()) {
Vec::<V>::schema(map)
}
}
}

Expand Down

0 comments on commit 79d0df6

Please sign in to comment.