Skip to content

Commit

Permalink
feat(builtins): json.obj_pairs, json.puts_pairs to build JSON objects…
Browse files Browse the repository at this point in the history
… from array of pairs (#1434)

* feat(builtins): impl json obj_array & puts_array

* feat(builtins): obj_array => obj_pairs

* feat(builtins): fix puts_pairs

* feat(builtins): log run-console logs to debug

* chore: fix formatting

* feat!(errors): format errors via Debug

* feat!(errors): format errors via Display AND Debug

* chore: fix tests

* chore: add tests on json obj_pairs puts_pairs
  • Loading branch information
folex committed Feb 2, 2023
1 parent 090ac84 commit 3539f67
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 136 deletions.
6 changes: 2 additions & 4 deletions crates/json-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
unreachable_patterns
)]

use std::fmt::Display;

use serde_json::Value as JValue;

pub mod base64_serde;
Expand All @@ -48,6 +46,6 @@ pub fn into_array(v: JValue) -> Option<Vec<JValue>> {
}

/// Converts an error into IValue::String
pub fn err_as_value<E: Display>(err: E) -> JValue {
JValue::String(format!("Error: {err}"))
pub fn err_as_value<E: core::fmt::Debug + core::fmt::Display>(err: E) -> JValue {
JValue::String(format!("Error: {err}\n{err:?}"))
}
110 changes: 1 addition & 109 deletions crates/libp2p/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,75 +39,10 @@ pub mod peerid_serializer {
}
}

// waiting for https://github.com/serde-rs/serde/issues/723 that will make UX better
pub mod provider_serializer {
use libp2p::{core::Multiaddr, PeerId};
use serde::{
de::{SeqAccess, Visitor},
ser::SerializeSeq,
Deserializer, Serializer,
};
use std::{fmt, str::FromStr};

pub fn serialize<S>(value: &[(Multiaddr, PeerId)], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(2 * value.len()))?;
for (multiaddr, peerid) in value {
seq.serialize_element(multiaddr)?;
seq.serialize_element(&peerid.to_base58())?;
}
seq.end()
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<(Multiaddr, PeerId)>, D::Error>
where
D: Deserializer<'de>,
{
struct VecVisitor;
impl<'de> Visitor<'de> for VecVisitor {
type Value = Vec<(Multiaddr, PeerId)>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[Multiaddr, PeerId]")
}

fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut vec = Vec::new();

let mut raw_multiaddr = seq.next_element::<Multiaddr>()?;
while raw_multiaddr.is_some() {
let multiaddr = raw_multiaddr.unwrap();
let peer_id_bytes = seq.next_element::<String>()?;

if let Some(peer_id) = peer_id_bytes {
let peer_id = PeerId::from_str(&peer_id).map_err(|e| {
serde::de::Error::custom(format!(
"peer id deserialization failed for {e:?}"
))
})?;
vec.push((multiaddr, peer_id));
} else {
// Multiaddr deserialization's been successfull, but PeerId hasn't - return a error
return Err(serde::de::Error::custom("failed to deserialize PeerId"));
}

raw_multiaddr = seq.next_element::<Multiaddr>()?;
}

Ok(vec)
}
}

deserializer.deserialize_seq(VecVisitor {})
}
}

#[cfg(test)]
mod tests {
use crate::RandomPeerId;
use libp2p::{core::Multiaddr, PeerId};
use libp2p::PeerId;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

Expand Down Expand Up @@ -180,47 +115,4 @@ mod tests {
);
assert_eq!(deserialized_test.unwrap(), test);
}

#[test]
fn providers() {
use super::provider_serializer;
use std::net::Ipv4Addr;

#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
struct Test {
#[serde(with = "provider_serializer")]
providers: Vec<(Multiaddr, PeerId)>,
}

let mut providers = Vec::new();
let mut test_peer_ids = Vec::new();
providers.push((
Multiaddr::from(Ipv4Addr::new(0, 0, 0, 0)),
PeerId::from_str("QmY28NSCefB532XbERtnKHadexGuNzAfYnh5fJk6qhLsSi").unwrap(),
));

for i in 1..=255 {
let peer_id = RandomPeerId::random();

providers.push((Multiaddr::from(Ipv4Addr::new(i, i, i, i)), peer_id));
test_peer_ids.push(peer_id);
}

let test = Test { providers };

let serialized_test = serde_json::to_value(test.clone());
assert!(
serialized_test.is_ok(),
"failed to serialize test struct: {}",
serialized_test.err().unwrap()
);

let deserialized_test = serde_json::from_value::<Test>(serialized_test.unwrap());
assert!(
deserialized_test.is_ok(),
"failed to deserialize test struct: {}",
deserialized_test.err().unwrap()
);
assert_eq!(deserialized_test.unwrap(), test);
}
}
4 changes: 2 additions & 2 deletions crates/log-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
pub fn enable_logs() {
use log::LevelFilter::*;

std::env::set_var("WASM_LOG", "trace");
std::env::set_var("WASM_LOG", "info");

env_logger::builder()
.format_timestamp_millis()
.filter_level(log::LevelFilter::Off)
.filter_level(log::LevelFilter::Info)
.filter(Some("script_storage"), Trace)
.filter(Some("script_storage"), Trace)
.filter(Some("sorcerer"), Trace)
Expand Down
5 changes: 5 additions & 0 deletions crates/particle-args/src/args_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use json_utils::err_as_value;

use eyre::Report;
use serde_json::{json, Value as JValue};
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
Expand Down Expand Up @@ -66,6 +67,10 @@ impl JError {
pub fn new(msg: impl AsRef<str>) -> Self {
Self(json!(msg.as_ref()))
}

pub fn from_eyre(err: Report) -> Self {
JError(err_as_value(err))
}
}

impl From<JError> for JValue {
Expand Down
96 changes: 91 additions & 5 deletions crates/particle-node-tests/tests/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,13 +1502,97 @@ fn json_builtins() {
(call relay ("json" "obj") ["name" "nested_first" "num" 1] nested_first)
(call relay ("json" "obj") ["name" "nested_second" "num" 2] nested_second)
)
(call relay ("json" "obj") ["name" "outer_first" "num" 0 "nested" nested_first] outer_first)
(seq
(call relay ("json" "obj") ["name" "outer_first" "num" 0 "nested" nested_first] outer_first)
(seq
(seq
(seq
(new $single-pair
(seq
(seq
(ap "name" $single-pair)
(ap "outer_first" $single-pair)
)
(seq
(canon relay $single-pair #single-pair-1)
(ap #single-pair-1 $pairs)
)
)
)
(new $single-pair
(seq
(seq
(ap "num" $single-pair)
(ap 0 $single-pair)
)
(seq
(canon relay $single-pair #single-pair-2)
(ap #single-pair-2 $pairs)
)
)
)
)
(seq
(new $single-pair
(seq
(seq
(ap "nested" $single-pair)
(ap nested_first $single-pair)
)
(seq
(canon relay $single-pair #single-pair-3)
(ap #single-pair-3 $pairs)
)
)
)
(canon relay $pairs #pairs)
)
)
(call relay ("json" "obj_pairs") [#pairs] outer_first_pairs)
)
)
)
(seq
;; modify
(seq
(call relay ("json" "put") [outer_first "nested" nested_second] outer_tmp_second)
(call relay ("json" "puts") [outer_tmp_second "name" "outer_second" "num" 3] outer_second)
(seq
(call relay ("json" "put") [outer_first "nested" nested_second] outer_tmp_second)
(call relay ("json" "puts") [outer_tmp_second "name" "outer_second" "num" 3] outer_second)
)
(new $puts-pairs
(seq
(seq
(new $single-pair
(seq
(seq
(ap "name" $single-pair)
(ap "outer_second" $single-pair)
)
(seq
(canon relay $single-pair #single-pair-4)
(ap #single-pair-4 $puts-pairs)
)
)
)
(new $single-pair
(seq
(seq
(ap "num" $single-pair)
(ap 3 $single-pair)
)
(seq
(canon relay $single-pair #single-pair-5)
(ap #single-pair-5 $puts-pairs)
)
)
)
)
(seq
(canon relay $puts-pairs #puts-pairs)
(call relay ("json" "puts_pairs") [outer_tmp_second #puts-pairs] outer_second_pairs)
)
)
)
)
;; stringify and parse
(seq
Expand All @@ -1519,11 +1603,11 @@ fn json_builtins() {
)
"#,
hashmap! {},
r"nested_first nested_second outer_first outer_second outer_first_string outer_first_parsed",
r"nested_first nested_second outer_first outer_second outer_first_string outer_first_parsed outer_first_pairs outer_second_pairs",
1,
).expect("execute script");

if let [nested_first, nested_second, outer_first, outer_second, outer_first_string, outer_first_parsed] =
if let [nested_first, nested_second, outer_first, outer_second, outer_first_string, outer_first_parsed, outer_first_pairs, outer_second_pairs] =
result.as_slice()
{
let nf_expected = json!({"name": "nested_first", "num": 1});
Expand All @@ -1535,7 +1619,9 @@ fn json_builtins() {
assert_eq!(&nf_expected, nested_first);
assert_eq!(&ns_expected, nested_second);
assert_eq!(&of_expected, outer_first);
assert_eq!(&of_expected, outer_first_pairs);
assert_eq!(&os_expected, outer_second);
assert_eq!(&os_expected, outer_second_pairs);
assert_eq!(&of_expected.to_string(), outer_first_string);
assert_eq!(&of_expected, outer_first_parsed);
} else {
Expand Down
5 changes: 4 additions & 1 deletion crates/particle-node-tests/tests/script_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,9 @@ fn add_script_from_vault_wrong_vault() {
.as_slice()
{
let expected_error_prefix = "Local service error, ret_code is 1, error message is '\"Error: Incorrect vault path `/tmp/vault/another-particle-id/script";
assert!(error_msg.starts_with(expected_error_prefix));
assert!(
error_msg.starts_with(expected_error_prefix),
"expected:\n{expected_error_prefix}\ngot:\n{error_msg}"
);
}
}
29 changes: 19 additions & 10 deletions crates/particle-node-tests/tests/spells.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use serde_json::{json, Value as JValue};
use connected_client::ConnectedClient;
use created_swarm::make_swarms;
use fluence_spell_dtos::trigger_config::TriggerConfig;
use log_utils::enable_logs;
use service_modules::load_module;
use spell_event_bus::api::{TriggerInfo, TriggerInfoAqua, MAX_PERIOD_SEC};
use test_utils::create_service;
Expand Down Expand Up @@ -414,15 +415,20 @@ fn spell_install_fail_end_sec_past() {
.unwrap()
.as_slice()
{
let msg = "Local service error, ret_code is 1, error message is '\"Error: invalid config: end_sec is less than start_sec or in the past\"'";
assert!(error_msg.starts_with(msg));
let expected = "Local service error, ret_code is 1, error message is '\"Error: invalid config: end_sec is less than start_sec or in the past";
assert!(
error_msg.starts_with(expected),
"expected:\n{expected}\ngot:\n{error_msg}"
);
}
}

// Also the config considered invalid if the end_sec is less than start_sec.
// In this case we don't schedule a spell and return error.
#[test]
fn spell_install_fail_end_sec_before_start() {
enable_logs();

let swarms = make_swarms(1);
let mut client = ConnectedClient::connect_to(swarms[0].multiaddr.clone())
.wrap_err("connect client")
Expand Down Expand Up @@ -463,8 +469,11 @@ fn spell_install_fail_end_sec_before_start() {
.unwrap()
.as_slice()
{
let msg = "Local service error, ret_code is 1, error message is '\"Error: invalid config: end_sec is less than start_sec or in the past\"'";
assert!(error_msg.starts_with(msg));
let expected = "Local service error, ret_code is 1, error message is '\"Error: invalid config: end_sec is less than start_sec or in the past";
assert!(
error_msg.starts_with(expected),
"expected:\n{expected}\ngot:\n{error_msg}"
);
}
}

Expand Down Expand Up @@ -601,10 +610,10 @@ fn spell_remove_spell_as_service() {
.unwrap()
.as_slice()
{
let msg_end = "cannot call function 'remove_service': cannot remove a spell\"'";
let expected = "cannot call function 'remove_service': cannot remove a spell";
assert!(
msg.ends_with(msg_end),
"should end with `{msg_end}`, given msg `{msg}`"
msg.contains(expected),
"should contain `{expected}`, given msg `{msg}`"
);
}
}
Expand Down Expand Up @@ -644,10 +653,10 @@ fn spell_remove_service_as_spell() {
.unwrap()
.as_slice()
{
let msg_end = "cannot call function 'remove_spell': the service isn't a spell\"'";
let expected = "cannot call function 'remove_spell': the service isn't a spell";
assert!(
msg.ends_with(msg_end),
"should end with `{msg_end}`, given msg `{msg}`"
msg.contains(expected),
"should contain `{expected}`, given msg `{msg}`"
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions particle-builtins/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ where
("json", "puts") => wrap(json::puts(args)),
("json", "parse") => unary(args, |s: String| -> R<JValue, _> { json::parse(&s) }),
("json", "stringify") => unary(args, |v: JValue| -> R<String, _> { Ok(json::stringify(v)) }),
("json", "obj_pairs") => unary(args, |vs: Vec<(String, JValue)>| -> R<JValue, _> { json::obj_from_pairs(vs) }),
("json", "puts_pairs") => binary(args, |obj: JValue, vs: Vec<(String, JValue)>| -> R<JValue, _> { json::puts_from_pairs(obj, vs) }),

("run-console", "print") => wrap_unit(Ok(log::debug!(target: "run-console", "{}", json!(args.function_args)))),

_ => FunctionOutcome::NotDefined { args, params: particle },
}
Expand Down
Loading

0 comments on commit 3539f67

Please sign in to comment.