Skip to content

Commit 4205e9c

Browse files
committed
fix: return proper JSON from REST API
1 parent 1debc19 commit 4205e9c

File tree

1 file changed

+40
-6
lines changed

1 file changed

+40
-6
lines changed

node/rest/src/routes.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use super::*;
1717
use snarkos_node_router::{SYNC_LENIENCY, messages::UnconfirmedSolution};
1818
use snarkvm::{
1919
ledger::puzzle::Solution,
20-
prelude::{Address, Identifier, LimitedWriter, Plaintext, ToBytes, block::Transaction},
20+
prelude::{Address, Identifier, LimitedWriter, Plaintext, ToBytes, Value, block::Transaction},
2121
};
2222

2323
use indexmap::IndexMap;
@@ -212,13 +212,47 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
212212
// Check if metadata is requested and return the value with metadata if so.
213213
if metadata.metadata.unwrap_or(false) {
214214
return Ok(ErasedJson::pretty(json!({
215-
"data": mapping_value,
215+
"data": Self::value_to_json(mapping_value),
216216
"height": rest.ledger.latest_height(),
217217
})));
218218
}
219219

220-
// Return the value without metadata.
221-
Ok(ErasedJson::pretty(mapping_value))
220+
Ok(ErasedJson::pretty(Self::value_to_json(mapping_value)))
221+
}
222+
223+
/// Convert a `Value` to JSON.
224+
/// `Value` implements Serialize by just calling `to_string()` on the value, which leads to Display
225+
/// output which (sometimes) looks like JSON but isn't.
226+
/// We produce actual JSON by calling `serde_json::to_value` on the inner object.
227+
fn value_to_json(mapping_value: Option<Value<N>>) -> serde_json::Value {
228+
if let Some(mapping_value) = mapping_value {
229+
match mapping_value {
230+
Value::Plaintext(plaintext) => match plaintext {
231+
Plaintext::Array(array, _) => serde_json::to_value(&array).unwrap(),
232+
Plaintext::Struct(map, _) => serde_json::to_value(&map).unwrap(),
233+
Plaintext::Literal(literal, _) => serde_json::to_value(&literal).unwrap(),
234+
},
235+
Value::Record(record) => serde_json::to_value(&record).unwrap(),
236+
Value::Future(future) => serde_json::to_value(&future).unwrap(),
237+
}
238+
} else {
239+
json!("{}")
240+
}
241+
}
242+
243+
fn vec_to_json(mapping_values: &Vec<(Plaintext<N>, Value<N>)>) -> serde_json::Value {
244+
let mut json = serde_json::Map::new();
245+
for (ref key, ref value) in mapping_values {
246+
let key = match key {
247+
// TODO: not sure if keys can be anything other than literals
248+
Plaintext::Array(array, _) => serde_json::to_string(array).unwrap(),
249+
Plaintext::Struct(map, _) => serde_json::to_string(map).unwrap(),
250+
// We can not use json here as it would cause the key to be escaped
251+
Plaintext::Literal(literal, _) => literal.to_string(),
252+
};
253+
json.insert(key, Self::value_to_json(Some(value.clone())));
254+
}
255+
serde_json::Value::Object(json)
222256
}
223257

224258
// GET /<network>/program/{programID}/mapping/{mappingName}?all={true}&metadata={true}
@@ -243,13 +277,13 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
243277
// Check if metadata is requested and return the mapping with metadata if so.
244278
if metadata.metadata.unwrap_or(false) {
245279
return Ok(ErasedJson::pretty(json!({
246-
"data": mapping_values,
280+
"data": Self::vec_to_json(&mapping_values),
247281
"height": height,
248282
})));
249283
}
250284

251285
// Return the full mapping without metadata.
252-
Ok(ErasedJson::pretty(mapping_values))
286+
Ok(ErasedJson::pretty(Self::vec_to_json(&mapping_values)))
253287
}
254288
Ok(Err(err)) => Err(RestError(format!("Unable to read mapping - {err}"))),
255289
Err(err) => Err(RestError(format!("Unable to read mapping - {err}"))),

0 commit comments

Comments
 (0)