casper_rust_wasm_sdk/sdk/rpcs/
get_entity.rs

1#[cfg(target_arch = "wasm32")]
2use crate::types::identifier::block_identifier::BlockIdentifier;
3use crate::{
4    types::{
5        identifier::{block_identifier::BlockIdentifierInput, entity_identifier::EntityIdentifier},
6        sdk_error::SdkError,
7        verbosity::Verbosity,
8    },
9    SDK,
10};
11use casper_client::{
12    cli::{get_entity as get_entity_cli, parse::entity_identifier as parse_entity_identifier},
13    get_entity as get_entity_lib,
14    rpcs::results::GetAddressableEntityResult as _GetAddressableEntityResult,
15    JsonRpcId, SuccessResponse,
16};
17#[cfg(target_arch = "wasm32")]
18use gloo_utils::format::JsValueSerdeExt;
19use rand::Rng;
20#[cfg(target_arch = "wasm32")]
21use serde::{Deserialize, Serialize};
22#[cfg(target_arch = "wasm32")]
23use wasm_bindgen::prelude::*;
24
25// Define the GetAddressableEntityResult struct to wrap the result from Casper Client RPC call
26#[cfg(target_arch = "wasm32")]
27#[derive(Debug, Deserialize, Clone, Serialize)]
28#[wasm_bindgen]
29pub struct GetAddressableEntityResult(_GetAddressableEntityResult);
30
31// Implement conversions between GetAddressableEntityResult and _GetAddressableEntityResult
32#[cfg(target_arch = "wasm32")]
33impl From<GetAddressableEntityResult> for _GetAddressableEntityResult {
34    fn from(result: GetAddressableEntityResult) -> Self {
35        result.0
36    }
37}
38
39#[cfg(target_arch = "wasm32")]
40impl From<_GetAddressableEntityResult> for GetAddressableEntityResult {
41    fn from(result: _GetAddressableEntityResult) -> Self {
42        GetAddressableEntityResult(result)
43    }
44}
45
46#[cfg(target_arch = "wasm32")]
47#[wasm_bindgen]
48impl GetAddressableEntityResult {
49    // Define getters for various fields of GetAddressableEntityResult
50    #[wasm_bindgen(getter)]
51    pub fn api_version(&self) -> JsValue {
52        JsValue::from_serde(&self.0.api_version).unwrap()
53    }
54
55    #[wasm_bindgen(getter)]
56    pub fn entity_result(&self) -> JsValue {
57        JsValue::from_serde(&self.0.entity_result).unwrap()
58    }
59
60    #[wasm_bindgen(getter)]
61    pub fn merkle_proof(&self) -> String {
62        self.0.merkle_proof.clone()
63    }
64
65    #[wasm_bindgen(js_name = "toJson")]
66    pub fn to_json(&self) -> JsValue {
67        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
68    }
69}
70
71// Define options for the `get_entity` function
72#[derive(Debug, Deserialize, Clone, Default, Serialize)]
73#[cfg(target_arch = "wasm32")]
74#[wasm_bindgen(js_name = "getEntityOptions", getter_with_clone)]
75pub struct GetEntityOptions {
76    pub entity_identifier: Option<EntityIdentifier>,
77    pub entity_identifier_as_string: Option<String>,
78    pub maybe_block_id_as_string: Option<String>,
79    pub maybe_block_identifier: Option<BlockIdentifier>,
80    pub rpc_address: Option<String>,
81    pub verbosity: Option<Verbosity>,
82}
83
84#[cfg(target_arch = "wasm32")]
85#[wasm_bindgen]
86impl SDK {
87    // Deserialize options for `get_entity` from a JavaScript object
88    pub fn get_entity_options(&self, options: JsValue) -> Result<GetEntityOptions, JsError> {
89        options
90            .into_serde::<GetEntityOptions>()
91            .map_err(|err| JsError::new(&format!("Error deserializing options: {:?}", err)))
92    }
93
94    /// Retrieves entity information using the provided options.
95    ///
96    /// This function is an asynchronous JavaScript binding for the Rust `get_entity` method.
97    ///
98    /// # Arguments
99    ///
100    /// * `options` - An optional `GetEntityOptions` struct containing retrieval options, such as:
101    ///   - `entity_identifier`: Identifier for the entity.
102    ///   - `entity_identifier_as_string`: String representation of the entity identifier.
103    ///   - `maybe_block_id_as_string`: Optional string representation of the block ID.
104    ///   - `maybe_block_identifier`: Optional `BlockIdentifierInput` for specifying the block.
105    ///   - `verbosity`: Verbosity level for the output.
106    ///   - `rpc_address`: Address of the node to query.
107    ///
108    /// # Returns
109    ///
110    /// A `Result` containing either a `GetAddressableEntityResult` on success or a `JsError` on failure.
111    ///
112    /// # Errors
113    ///
114    /// Returns a `JsError` if there is an error during the retrieval process, such as issues with the provided options or network errors.
115    /// ```
116    #[wasm_bindgen(js_name = "get_entity")]
117    pub async fn get_entity_js_alias(
118        &self,
119        options: Option<GetEntityOptions>,
120    ) -> Result<GetAddressableEntityResult, JsError> {
121        let GetEntityOptions {
122            entity_identifier,
123            entity_identifier_as_string,
124            maybe_block_id_as_string,
125            maybe_block_identifier,
126            verbosity,
127            rpc_address,
128        } = options.unwrap_or_default();
129
130        let maybe_block_identifier = if let Some(maybe_block_identifier) = maybe_block_identifier {
131            Some(BlockIdentifierInput::BlockIdentifier(
132                maybe_block_identifier,
133            ))
134        } else {
135            maybe_block_id_as_string.map(BlockIdentifierInput::String)
136        };
137
138        let result = self
139            .get_entity(
140                entity_identifier,
141                entity_identifier_as_string,
142                maybe_block_identifier,
143                verbosity,
144                rpc_address,
145            )
146            .await;
147        match result {
148            Ok(data) => Ok(data.result.into()),
149            Err(err) => {
150                let err = &format!("Error occurred with {:?}", err);
151                Err(JsError::new(err))
152            }
153        }
154    }
155
156    // JavaScript alias for `get_entity`
157    #[wasm_bindgen(js_name = "state_get_entity")]
158    pub async fn state_get_entity(
159        &self,
160        options: Option<GetEntityOptions>,
161    ) -> Result<GetAddressableEntityResult, JsError> {
162        self.get_entity_js_alias(options).await
163    }
164}
165
166impl SDK {
167    /// Retrieves account information based on the provided options.
168    ///
169    /// # Arguments
170    ///
171    /// * `entity_identifier` - An optional `EntityIdentifier` for specifying the entity identifier.
172    /// * `entity_identifier_as_string` - An optional string representing the entity identifier.
173    /// * `maybe_block_identifier` - An optional `BlockIdentifierInput` for specifying a block identifier.
174    /// * `verbosity` - An optional `Verbosity` level for controlling the output verbosity.
175    /// * `rpc_address` - An optional string specifying the rpc address to use for the request.
176    ///
177    /// # Returns
178    ///
179    /// A `Result` containing either a `SuccessResponse<_GetAddressableEntityResult>` or a `SdkError` in case of an error.
180    ///
181    /// # Errors
182    ///
183    /// Returns a `SdkError` if there is an error during the retrieval process.
184    pub async fn get_entity(
185        &self,
186        entity_identifier: Option<EntityIdentifier>,
187        entity_identifier_as_string: Option<String>,
188        maybe_block_identifier: Option<BlockIdentifierInput>,
189        verbosity: Option<Verbosity>,
190        rpc_address: Option<String>,
191    ) -> Result<SuccessResponse<_GetAddressableEntityResult>, SdkError> {
192        let entity_identifier = if let Some(entity_identifier) = entity_identifier {
193            entity_identifier
194        } else if let Some(entity_identifier_as_string) = entity_identifier_as_string.clone() {
195            match parse_entity_identifier(&entity_identifier_as_string) {
196                Ok(parsed) => parsed.into(),
197                Err(err) => {
198                    return Err(err.into());
199                }
200            }
201        } else {
202            let err = "Error: Missing entity identifier".to_string();
203            return Err(SdkError::InvalidArgument {
204                context: "get_entity",
205                error: err,
206            });
207        };
208        if let Some(BlockIdentifierInput::String(maybe_block_id)) = maybe_block_identifier {
209            get_entity_cli(
210                &rand::thread_rng().gen::<u64>().to_string(),
211                &self.get_rpc_address(rpc_address),
212                self.get_verbosity(verbosity).into(),
213                &maybe_block_id,
214                &entity_identifier.to_string(),
215            )
216            .await
217            .map_err(SdkError::from)
218        } else {
219            let maybe_block_identifier =
220                if let Some(BlockIdentifierInput::BlockIdentifier(maybe_block_identifier)) =
221                    maybe_block_identifier
222                {
223                    Some(maybe_block_identifier)
224                } else {
225                    None
226                };
227            get_entity_lib(
228                JsonRpcId::from(rand::thread_rng().gen::<u64>().to_string()),
229                &self.get_rpc_address(rpc_address),
230                self.get_verbosity(verbosity).into(),
231                maybe_block_identifier.map(Into::into),
232                entity_identifier.into(),
233            )
234            .await
235            .map_err(SdkError::from)
236        }
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    use crate::{
244        helpers::public_key_from_secret_key,
245        types::{identifier::block_identifier::BlockIdentifier, public_key::PublicKey},
246    };
247    use sdk_tests::tests::helpers::{
248        get_enable_addressable_entity, get_network_constants, get_user_secret_key,
249    };
250
251    fn get_entity_identifier() -> EntityIdentifier {
252        let secret_key = get_user_secret_key(None).unwrap();
253        let account = public_key_from_secret_key(&secret_key).unwrap();
254        let public_key = PublicKey::new(&account).unwrap();
255
256        EntityIdentifier::from_entity_under_public_key(public_key)
257    }
258
259    #[tokio::test]
260    async fn test_get_entity_with_none_values() {
261        // Arrange
262        let sdk = SDK::new(None, None, None);
263        let error_message = "failed to parse node address as valid URL";
264        let entity_identifier = get_entity_identifier();
265
266        // Act
267        let result = sdk
268            .get_entity(Some(entity_identifier), None, None, None, None)
269            .await;
270
271        // Assert
272        assert!(result.is_err());
273        let err_string = result.err().unwrap().to_string();
274        assert!(err_string.contains(error_message));
275    }
276
277    #[tokio::test]
278    async fn test_get_entity_with_missing_entity() {
279        // Arrange
280        let sdk = SDK::new(None, None, None);
281        let error_message = "Error: Missing entity identifier";
282
283        // Act
284        let result = sdk.get_entity(None, None, None, None, None).await;
285
286        // Assert
287        assert!(result.is_err());
288        let err_string = result.err().unwrap().to_string();
289        assert!(err_string.contains(error_message));
290    }
291
292    #[tokio::test]
293    async fn test_get_entity_with_entity_identifier() {
294        if !get_enable_addressable_entity() {
295            return;
296        }
297        // Arrange
298        let sdk = SDK::new(None, None, None);
299        let entity_identifier = get_entity_identifier();
300        let verbosity = Some(Verbosity::High);
301        let (rpc_address, _, _, _, _) = get_network_constants();
302
303        // Act
304        let result = sdk
305            .get_entity(
306                Some(entity_identifier),
307                None,
308                None,
309                verbosity,
310                Some(rpc_address),
311            )
312            .await;
313        // Assert
314        assert!(result.is_ok());
315    }
316
317    #[tokio::test]
318    async fn test_get_entity_with_entity_identifier_as_string() {
319        if !get_enable_addressable_entity() {
320            return;
321        }
322        // Arrange
323        let sdk = SDK::new(None, None, None);
324        let entity_identifier_as_string = get_entity_identifier().to_string();
325        let verbosity = Some(Verbosity::High);
326        let (rpc_address, _, _, _, _) = get_network_constants();
327
328        // Act
329        let result = sdk
330            .get_entity(
331                None,
332                Some(entity_identifier_as_string),
333                None,
334                verbosity,
335                Some(rpc_address),
336            )
337            .await;
338
339        // Assert
340        assert!(result.is_ok());
341    }
342
343    #[tokio::test]
344    async fn test_get_entity_with_block_identifier() {
345        if !get_enable_addressable_entity() {
346            return;
347        }
348        // Arrange
349        let sdk = SDK::new(None, None, None);
350        let block_identifier =
351            BlockIdentifierInput::BlockIdentifier(BlockIdentifier::from_height(1));
352        let entity_identifier = get_entity_identifier();
353        let verbosity = Some(Verbosity::High);
354        let (rpc_address, _, _, _, _) = get_network_constants();
355
356        // Act
357        let result = sdk
358            .get_entity(
359                Some(entity_identifier),
360                None,
361                Some(block_identifier),
362                verbosity,
363                Some(rpc_address),
364            )
365            .await;
366
367        // Assert
368        assert!(result.is_ok());
369    }
370
371    #[tokio::test]
372    async fn test_get_entity_with_error() {
373        // Arrange
374        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
375        let entity_identifier = get_entity_identifier();
376        let error_message = "error sending request for url (http://localhost/rpc)";
377
378        // Act
379        let result = sdk
380            .get_entity(Some(entity_identifier), None, None, None, None)
381            .await;
382
383        // Assert
384        assert!(result.is_err());
385        let err_string = result.err().unwrap().to_string();
386        assert!(err_string.contains(error_message));
387    }
388}