Skip to content

Commit 9f35aa2

Browse files
committed
fix: return proper JSON from REST API
1 parent 375b05e commit 9f35aa2

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;
@@ -220,13 +220,47 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
220220
// Check if metadata is requested and return the value with metadata if so.
221221
if metadata.metadata.unwrap_or(false) {
222222
return Ok(ErasedJson::pretty(json!({
223-
"data": mapping_value,
223+
"data": Self::value_to_json(mapping_value),
224224
"height": rest.ledger.latest_height(),
225225
})));
226226
}
227227

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

232266
// GET /<network>/program/{programID}/mapping/{mappingName}?all={true}&metadata={true}
@@ -251,13 +285,13 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
251285
// Check if metadata is requested and return the mapping with metadata if so.
252286
if metadata.metadata.unwrap_or(false) {
253287
return Ok(ErasedJson::pretty(json!({
254-
"data": mapping_values,
288+
"data": Self::vec_to_json(&mapping_values),
255289
"height": height,
256290
})));
257291
}
258292

259293
// Return the full mapping without metadata.
260-
Ok(ErasedJson::pretty(mapping_values))
294+
Ok(ErasedJson::pretty(Self::vec_to_json(&mapping_values)))
261295
}
262296
Ok(Err(err)) => Err(RestError(format!("Unable to read mapping - {err}"))),
263297
Err(err) => Err(RestError(format!("Unable to read mapping - {err}"))),

0 commit comments

Comments
 (0)