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        let random_id = rand::thread_rng().gen::<u64>().to_string();
209        if let Some(BlockIdentifierInput::String(maybe_block_id)) = maybe_block_identifier {
210            get_entity_cli(
211                &random_id,
212                &self.get_rpc_address(rpc_address),
213                self.get_verbosity(verbosity).into(),
214                &maybe_block_id,
215                &entity_identifier.to_string(),
216            )
217            .await
218            .map_err(SdkError::from)
219        } else {
220            let maybe_block_identifier =
221                if let Some(BlockIdentifierInput::BlockIdentifier(maybe_block_identifier)) =
222                    maybe_block_identifier
223                {
224                    Some(maybe_block_identifier)
225                } else {
226                    None
227                };
228            get_entity_lib(
229                JsonRpcId::from(random_id),
230                &self.get_rpc_address(rpc_address),
231                self.get_verbosity(verbosity).into(),
232                maybe_block_identifier.map(Into::into),
233                entity_identifier.into(),
234            )
235            .await
236            .map_err(SdkError::from)
237        }
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::{
245        helpers::public_key_from_secret_key,
246        types::{identifier::block_identifier::BlockIdentifier, public_key::PublicKey},
247    };
248    use sdk_tests::tests::helpers::{
249        get_enable_addressable_entity, get_network_constants, get_user_secret_key,
250    };
251
252    fn get_entity_identifier() -> EntityIdentifier {
253        let secret_key = get_user_secret_key(None).unwrap();
254        let account = public_key_from_secret_key(&secret_key).unwrap();
255        let public_key = PublicKey::new(&account).unwrap();
256
257        EntityIdentifier::from_entity_under_public_key(public_key)
258    }
259
260    #[tokio::test]
261    async fn test_get_entity_with_none_values() {
262        // Arrange
263        let sdk = SDK::new(None, None, None);
264        let error_message = "failed to parse node address as valid URL";
265        let entity_identifier = get_entity_identifier();
266
267        // Act
268        let result = sdk
269            .get_entity(Some(entity_identifier), None, None, None, None)
270            .await;
271
272        // Assert
273        assert!(result.is_err());
274        let err_string = result.err().unwrap().to_string();
275        assert!(err_string.contains(error_message));
276    }
277
278    #[tokio::test]
279    async fn test_get_entity_with_missing_entity() {
280        // Arrange
281        let sdk = SDK::new(None, None, None);
282        let error_message = "Error: Missing entity identifier";
283
284        // Act
285        let result = sdk.get_entity(None, None, None, None, None).await;
286
287        // Assert
288        assert!(result.is_err());
289        let err_string = result.err().unwrap().to_string();
290        assert!(err_string.contains(error_message));
291    }
292
293    #[tokio::test]
294    async fn test_get_entity_with_entity_identifier() {
295        if !get_enable_addressable_entity() {
296            return;
297        }
298        // Arrange
299        let sdk = SDK::new(None, None, None);
300        let entity_identifier = get_entity_identifier();
301        let verbosity = Some(Verbosity::High);
302        let (rpc_address, _, _, _, _) = get_network_constants();
303
304        // Act
305        let result = sdk
306            .get_entity(
307                Some(entity_identifier),
308                None,
309                None,
310                verbosity,
311                Some(rpc_address),
312            )
313            .await;
314        // Assert
315        assert!(result.is_ok());
316    }
317
318    #[tokio::test]
319    async fn test_get_entity_with_entity_identifier_as_string() {
320        if !get_enable_addressable_entity() {
321            return;
322        }
323        // Arrange
324        let sdk = SDK::new(None, None, None);
325        let entity_identifier_as_string = get_entity_identifier().to_string();
326        let verbosity = Some(Verbosity::High);
327        let (rpc_address, _, _, _, _) = get_network_constants();
328
329        // Act
330        let result = sdk
331            .get_entity(
332                None,
333                Some(entity_identifier_as_string),
334                None,
335                verbosity,
336                Some(rpc_address),
337            )
338            .await;
339
340        // Assert
341        assert!(result.is_ok());
342    }
343
344    #[tokio::test]
345    async fn test_get_entity_with_block_identifier() {
346        if !get_enable_addressable_entity() {
347            return;
348        }
349        // Arrange
350        let sdk = SDK::new(None, None, None);
351        let block_identifier =
352            BlockIdentifierInput::BlockIdentifier(BlockIdentifier::from_height(1));
353        let entity_identifier = get_entity_identifier();
354        let verbosity = Some(Verbosity::High);
355        let (rpc_address, _, _, _, _) = get_network_constants();
356
357        // Act
358        let result = sdk
359            .get_entity(
360                Some(entity_identifier),
361                None,
362                Some(block_identifier),
363                verbosity,
364                Some(rpc_address),
365            )
366            .await;
367
368        // Assert
369        assert!(result.is_ok());
370    }
371
372    #[tokio::test]
373    async fn test_get_entity_with_error() {
374        // Arrange
375        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
376        let entity_identifier = get_entity_identifier();
377        let error_message = "error sending request";
378
379        // Act
380        let result = sdk
381            .get_entity(Some(entity_identifier), None, None, None, None)
382            .await;
383
384        // Assert
385        assert!(result.is_err());
386        let err_string = result.err().unwrap().to_string();
387        assert!(err_string.contains(error_message));
388    }
389}