diff --git a/node/rest/src/lib.rs b/node/rest/src/lib.rs index 202dbcca0f..5b5b652ad7 100644 --- a/node/rest/src/lib.rs +++ b/node/rest/src/lib.rs @@ -133,6 +133,7 @@ impl, R: Routing> Rest { // All the endpoints before the call to `route_layer` are protected with JWT auth. .route(&format!("/{network}/node/address"), get(Self::get_node_address)) + .route(&format!("/{network}/program/:id/mapping/:name"), get(Self::get_mapping_values)) .route_layer(middleware::from_fn(auth_middleware)) // ----------------- DEPRECATED ROUTES ----------------- diff --git a/node/rest/src/routes.rs b/node/rest/src/routes.rs index 500cbf9b96..23b8072878 100644 --- a/node/rest/src/routes.rs +++ b/node/rest/src/routes.rs @@ -33,10 +33,11 @@ pub(crate) struct BlockRange { end: u32, } -/// The `get_mapping_value` query object. -#[derive(Deserialize, Serialize)] +/// The query object for `get_mapping_value` and `get_mapping_values`. +#[derive(Copy, Clone, Deserialize, Serialize)] pub(crate) struct Metadata { - metadata: bool, + metadata: Option, + all: Option, } impl, R: Routing> Rest { @@ -230,13 +231,13 @@ impl, R: Routing> Rest { pub(crate) async fn get_mapping_value( State(rest): State, Path((id, name, key)): Path<(ProgramID, Identifier, Plaintext)>, - metadata: Option>, + metadata: Query, ) -> Result { // Retrieve the mapping value. let mapping_value = rest.ledger.vm().finalize_store().get_value_confirmed(id, name, &key)?; // Check if metadata is requested and return the value with metadata if so. - if metadata.map(|q| q.metadata).unwrap_or(false) { + if metadata.metadata.unwrap_or(false) { return Ok(ErasedJson::pretty(json!({ "data": mapping_value, "height": rest.ledger.latest_height(), @@ -247,6 +248,41 @@ impl, R: Routing> Rest { Ok(ErasedJson::pretty(mapping_value)) } + // GET //program/{programID}/mapping/{mappingName}?all={true}&metadata={true} + pub(crate) async fn get_mapping_values( + State(rest): State, + Path((id, name)): Path<(ProgramID, Identifier)>, + metadata: Query, + ) -> Result { + // Return an error if the `all` query parameter is not set to `true`. + if metadata.all != Some(true) { + return Err(RestError("Invalid query parameter. At this time, 'all=true' must be included".to_string())); + } + + // Retrieve the latest height. + let height = rest.ledger.latest_height(); + + // Retrieve all the mapping values from the mapping. + match tokio::task::spawn_blocking(move || rest.ledger.vm().finalize_store().get_mapping_confirmed(id, name)) + .await + { + Ok(Ok(mapping_values)) => { + // Check if metadata is requested and return the mapping with metadata if so. + if metadata.metadata.unwrap_or(false) { + return Ok(ErasedJson::pretty(json!({ + "data": mapping_values, + "height": height, + }))); + } + + // Return the full mapping without metadata. + Ok(ErasedJson::pretty(mapping_values)) + } + Ok(Err(err)) => Err(RestError(format!("Unable to read mapping - {err}"))), + Err(err) => Err(RestError(format!("Unable to read mapping - {err}"))), + } + } + // GET //statePath/{commitment} pub(crate) async fn get_state_path_for_commitment( State(rest): State,