Skip to content

Commit 60336c9

Browse files
authored
Fixed chapter 07 (#12)
1 parent 2dda500 commit 60336c9

File tree

1 file changed

+133
-118
lines changed

1 file changed

+133
-118
lines changed

docs/tutorial/platform/07-first-contract-query.md

Lines changed: 133 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -5,165 +5,176 @@ description: Add a function to your smart contract so you can query addresses by
55

66
# First contract Query
77

8-
In the previous section, you added a message to register an address under a name in storage. In this section, you make it possible to easily query for said stored values.
8+
In the previous section, you added a message to register an address under a name in storage.
9+
In this section, you make it possible to easily query for said stored values.
910

10-
<HighlightBox type="info" title="Exercise progression">
11+
:::info Exercise progression
1112

12-
If you skipped the previous section, you can just switch the project to its [`first-execute-message`](https://github.com/b9lab/cw-my-nameservice/tree/first-execute-message) branch and take it from there.
13+
If you skipped the previous section, you can just switch the project to its
14+
[`first-execute-message`](https://github.com/b9lab/cw-my-nameservice/tree/first-execute-message)
15+
branch and take it from there.
1316

14-
</HighlightBox>
17+
:::
1518

1619
## The query message and response
1720

1821
To query from storage, you add a specific query message and its corresponding response. Add a `QueryResponse` import to `src/msg.rs`:
1922

20-
<CodeBlock title="src/msg.rs">
21-
```diff-rs
22-
- use cosmwasm_schema::cw_serde;
23-
+ use cosmwasm_schema::{cw_serde, QueryResponses};
24-
```
25-
</CodeBlock>
23+
```rust title="src/msg.rs"
24+
//diff-del
25+
- use cosmwasm_schema::cw_serde;
26+
//diff-add
27+
+ use cosmwasm_schema::{cw_serde, QueryResponses};
28+
```
2629

2730
Then add the new enum and struct:
2831

29-
<CodeBlock title="src/msg.rs">
30-
```rust
31-
#[cw_serde]
32-
#[derive(QueryResponses)]
33-
pub enum QueryMsg {
34-
#[returns(ResolveRecordResponse)]
35-
ResolveRecord { name: String },
36-
}
37-
38-
#[cw_serde]
39-
pub struct ResolveRecordResponse {
40-
pub address: Option<String>,
41-
}
42-
```
43-
</CodeBlock>
32+
```rust title="src/msg.rs"
33+
#[cw_serde]
34+
#[derive(QueryResponses)]
35+
pub enum QueryMsg {
36+
#[returns(ResolveRecordResponse)]
37+
ResolveRecord { name: String },
38+
}
39+
40+
#[cw_serde]
41+
pub struct ResolveRecordResponse {
42+
pub address: Option<String>,
43+
}
44+
```
4445

4546
Note how:
4647

47-
* As with the transaction message, the `QueryMsg` is an enum.
48-
* The `ResolveRecord` type mentions the type it returns with the use of the `returns` macro.
49-
* `QueryResponses` is a [macro](https://github.com/CosmWasm/cosmwasm/blob/main/packages/schema-derive/src/lib.rs#L12).
50-
* `ResolveRecordResponse` contains an `Option<String>` to account for the fact that a missing owner is a valid result when resolving a name.
51-
* `ResolveRecordResponse` otherwise looks very much like a `NameRecord`, but it could be different and collect different values from different places, depending on what is needed with this query.
48+
- As with the transaction message, the `QueryMsg` is an enum.
49+
- The `ResolveRecord` type mentions the type it returns with the use of the `returns` macro.
50+
- `QueryResponses` is a [macro](https://github.com/CosmWasm/cosmwasm/blob/main/packages/schema-derive/src/lib.rs#L12).
51+
- `ResolveRecordResponse` contains an `Option<String>` to account for the fact that a missing owner is a valid result when resolving a name.
52+
- `ResolveRecordResponse` otherwise looks very much like a `NameRecord`, but it could be different and collect
53+
different values from different places, depending on what is needed with this query.
5254

5355
## The query function
5456

5557
You define the message handling in `src/contract.rs`. Adjust the imports:
5658

57-
<CodeBlock title="src/contract.rs">
58-
```diff-rs
59-
use crate::{
60-
error::ContractError,
61-
- msg::{ExecuteMsg, InstantiateMsg},
62-
+ msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ResolveRecordResponse},
63-
state::{NameRecord, NAME_RESOLVER},
64-
};
65-
- use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response};
66-
+ use cosmwasm_std::{
67-
+ entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
68-
+ };
59+
60+
```rust title="src/contract.rs"
61+
use crate::{
62+
error::ContractError,
63+
//diff-del
64+
- msg::{ExecuteMsg, InstantiateMsg},
65+
//diff-add
66+
+ msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ResolveRecordResponse},
67+
state::{NameRecord, NAME_RESOLVER},
68+
};
69+
//diff-del
70+
- use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response};
71+
//diff-add-start
72+
+ use cosmwasm_std::{
73+
+ entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
74+
+ };
75+
//diff-add-end
6976
```
70-
</CodeBlock>
7177

7278
Then, below the `execute` function, you add the query functions:
7379

74-
<CodeBlock title="src/contract.rs">
75-
```rust
76-
#[cfg_attr(not(feature = "library"), entry_point)]
77-
pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> StdResult<Binary> {
78-
match msg {
79-
QueryMsg::ResolveRecord { name } => query_resolve_record(deps, name),
80-
}
80+
```rust title="src/contract.rs"
81+
#[cfg_attr(not(feature = "library"), entry_point)]
82+
pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> StdResult<Binary> {
83+
match msg {
84+
QueryMsg::ResolveRecord { name } => query_resolve_record(deps, name),
8185
}
86+
}
8287

83-
fn query_resolve_record(deps: Deps, name: String) -> StdResult<Binary> {
84-
let key = name.as_bytes();
88+
fn query_resolve_record(deps: Deps, name: String) -> StdResult<Binary> {
89+
let key = name.as_bytes();
8590

86-
let address = NAME_RESOLVER
87-
.may_load(deps.storage, key)?
88-
.map(|record| record.owner.to_string());
91+
let address = NAME_RESOLVER
92+
.may_load(deps.storage, key)?
93+
.map(|record| record.owner.to_string());
8994

90-
let resp = ResolveRecordResponse { address };
95+
let resp = ResolveRecordResponse { address };
9196

92-
to_json_binary(&resp)
93-
}
94-
```
95-
</CodeBlock>
97+
to_json_binary(&resp)
98+
}
99+
```
96100

97101
Note how:
98102

99-
* Just as for the `execute` function, the `query` function is only here to dispatch to other functions depending on the message variant.
100-
* Unlike the `execute` function, it takes a non-mutable `Deps`. Indeed, a query is not meant to modify storage, and Rust can catch such errors at compilation instead of run time.
101-
* The function uses the `.may_load` method to handle potential errors gracefully. See Rust's `?` conditional `return`.
102-
* The `address` variable is an `Option<String>` because a missing value in storage is a valid response.
103-
* The return type is JSON binary, that you create by calling the standard `serde` function `to_json_binary`.
103+
- Just as for the `execute` function, the `query` function is only here to dispatch to other functions
104+
depending on the message variant.
105+
- Unlike the `execute` function, it takes a non-mutable `Deps`. Indeed, a query is not meant to modify storage,
106+
and Rust can catch such errors at compilation instead of run time.
107+
- The function uses the `.may_load` method to handle potential errors gracefully. See Rust's `?` conditional `return`.
108+
- The `address` variable is an `Option<String>` because a missing value in storage is a valid response.
109+
- The return type is JSON binary, that you create by calling the standard `serde` function `to_json_binary`.
104110

105111
With this done, you can now query registered addresses by their names.
106112

107113
## Unit testing
108114

109115
It's time for your third unit test. In `src/contract.rs`, add the following:
110116

111-
<CodeBlock title="src/contract.rs">
112-
```diff-rs
113-
...
114-
115-
#[cfg(test)]
116-
mod tests {
117-
use crate::{
118-
- msg::{ExecuteMsg, InstantiateMsg},
119-
+ msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
120-
state::{NameRecord, NAME_RESOLVER},
121-
};
122-
- use cosmwasm_std::{testing, Addr, Response};
123-
+ use cosmwasm_std::{testing, Addr, Binary, Response};
124-
125-
...
126-
127-
#[test]
128-
fn test_execute() {
129-
...
130-
}
131-
132-
+ #[test]
133-
+ fn test_query() {
134-
+ // Arrange
135-
+ let mut mocked_deps_mut = testing::mock_dependencies();
136-
+ let mocked_env = testing::mock_env();
137-
+ let name = "alice".to_owned();
138-
+ let mocked_addr_value = "addr".to_owned();
139-
+ let mocked_addr = Addr::unchecked(mocked_addr_value.clone());
140-
+ let mocked_msg_info = testing::message_info(&mocked_addr, &[]);
141-
+ let _ = super::execute_register(mocked_deps_mut.as_mut(), mocked_msg_info, name.clone())
142-
+ .expect("Failed to register alice");
143-
+ let query_msg = QueryMsg::ResolveRecord { name };
144-
+
145-
+ // Act
146-
+ let query_result = super::query(mocked_deps_mut.as_ref(), mocked_env, query_msg);
147-
+
148-
+ // Assert
149-
+ assert!(query_result.is_ok(), "Failed to query alice name");
150-
+ let expected_response = format!(r#"{{"address":"{mocked_addr_value}"}}"#);
151-
+ let expected = Binary::new(expected_response.as_bytes().to_vec());
152-
+ assert_eq!(query_result.unwrap(), expected);
153-
+ }
117+
```rust title="src/contract.rs"
118+
...
119+
120+
#[cfg(test)]
121+
mod tests {
122+
use crate::{
123+
//diff-del
124+
- msg::{ExecuteMsg, InstantiateMsg},
125+
//diff-add
126+
+ msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
127+
state::{NameRecord, NAME_RESOLVER},
128+
};
129+
//diff-del
130+
- use cosmwasm_std::{testing, Addr, Response};
131+
//diff-add
132+
+ use cosmwasm_std::{testing, Addr, Binary, Response};
133+
134+
...
135+
136+
#[test]
137+
fn test_execute() {
138+
...
154139
}
155-
```
156-
</CodeBlock>
140+
141+
//diff-add-start
142+
+ #[test]
143+
+ fn test_query() {
144+
+ // Arrange
145+
+ let mut mocked_deps_mut = testing::mock_dependencies();
146+
+ let mocked_env = testing::mock_env();
147+
+ let name = "alice".to_owned();
148+
+ let mocked_addr_value = "addr".to_owned();
149+
+ let mocked_addr = Addr::unchecked(mocked_addr_value.clone());
150+
+ let mocked_msg_info = testing::message_info(&mocked_addr, &[]);
151+
+ let _ = super::execute_register(mocked_deps_mut.as_mut(), mocked_msg_info, name.clone())
152+
+ .expect("Failed to register alice");
153+
+ let query_msg = QueryMsg::ResolveRecord { name };
154+
+
155+
+ // Act
156+
+ let query_result = super::query(mocked_deps_mut.as_ref(), mocked_env, query_msg);
157+
+
158+
+ // Assert
159+
+ assert!(query_result.is_ok(), "Failed to query alice name");
160+
+ let expected_response = format!(r#"{{"address":"{mocked_addr_value}"}}"#);
161+
+ let expected = Binary::new(expected_response.as_bytes().to_vec());
162+
+ assert_eq!(query_result.unwrap(), expected);
163+
+ }
164+
//diff-add-end
165+
}
166+
```
157167

158168
Note that:
159169

160-
* When arranging for the test, you keep the address string value for reuse.
161-
* The expected string reads as a JSON, and that it is created with escaped characters made possible with `{{` and the raw string marker `r#...#`.
162-
* `Binary::new(expected_response.as_bytes().to_vec())` converts to a binary.
170+
- When arranging for the test, you keep the address string value for reuse.
171+
- The expected string reads as a JSON, and that it is created with escaped characters
172+
made possible with `{{` and the raw string marker `r#...#`.
173+
- `Binary::new(expected_response.as_bytes().to_vec())` converts to a binary.
163174

164175
After you run `cargo test`, it should print its success in the output:
165176

166-
```txt
177+
```text
167178
...
168179
running 3 tests
169180
test contract::tests::test_instantiate ... ok
@@ -176,10 +187,14 @@ test contract::tests::test_query ... ok
176187

177188
You have created a query message and added its handling so that users and other smart contracts can check the registration status of names.
178189

179-
<HighlightBox type="info" title="Exercise progression">
190+
:::info Exercise progression
180191

181-
At this stage, you should have something similar to the [`first-query-message`](https://github.com/b9lab/cw-my-nameservice/tree/first-query-message) branch, with [this](https://github.com/b9lab/cw-my-nameservice/compare/first-execute-message..first-query-message) as the diff.
192+
At this stage, you should have something similar to the
193+
[`first-query-message`](https://github.com/b9lab/cw-my-nameservice/tree/first-query-message) branch,
194+
with [this](https://github.com/b9lab/cw-my-nameservice/compare/first-execute-message..first-query-message) as the diff.
182195

183-
</HighlightBox>
196+
:::
184197

185-
So far your unit tests have only tested functions in isolation and run within Rust, without touching WebAssembly or CosmWasm. You expand a bit in the next section by having your functions interact with a mocked app chain.
198+
So far your unit tests have only tested functions in isolation and run within Rust,
199+
without touching WebAssembly or CosmWasm. You expand a bit in the next section by having
200+
your functions interact with a mocked app chain.

0 commit comments

Comments
 (0)