sui_rpc_api/grpc/v2/ledger_service/
get_object.rs1use crate::ErrorReason;
5use crate::RpcError;
6use crate::RpcService;
7use crate::error::ObjectNotFoundError;
8use prost_types::FieldMask;
9use sui_rpc::field::FieldMaskTree;
10use sui_rpc::field::FieldMaskUtil;
11use sui_rpc::merge::Merge;
12use sui_rpc::proto::google::rpc::bad_request::FieldViolation;
13use sui_rpc::proto::sui::rpc::v2::BatchGetObjectsRequest;
14use sui_rpc::proto::sui::rpc::v2::BatchGetObjectsResponse;
15use sui_rpc::proto::sui::rpc::v2::GetObjectRequest;
16use sui_rpc::proto::sui::rpc::v2::GetObjectResponse;
17use sui_rpc::proto::sui::rpc::v2::GetObjectResult;
18use sui_rpc::proto::sui::rpc::v2::Object;
19use sui_sdk_types::Address;
20
21pub const READ_MASK_DEFAULT: &str = "object_id,version,digest";
22
23type ValidationResult = Result<(Vec<(Address, Option<u64>)>, FieldMaskTree), RpcError>;
24
25pub fn validate_get_object_requests(
26 requests: Vec<(Option<String>, Option<u64>)>,
27 read_mask: Option<FieldMask>,
28) -> ValidationResult {
29 let read_mask = {
30 let read_mask = read_mask.unwrap_or_else(|| FieldMask::from_str(READ_MASK_DEFAULT));
31 read_mask.validate::<Object>().map_err(|path| {
32 FieldViolation::new("read_mask")
33 .with_description(format!("invalid read_mask path: {path}"))
34 .with_reason(ErrorReason::FieldInvalid)
35 })?;
36 FieldMaskTree::from(read_mask)
37 };
38 let requests = requests
39 .into_iter()
40 .enumerate()
41 .map(|(idx, (object_id, version))| {
42 let object_id = object_id
43 .as_ref()
44 .ok_or_else(|| {
45 FieldViolation::new("object_id")
46 .with_reason(ErrorReason::FieldMissing)
47 .nested_at("requests", idx)
48 })?
49 .parse()
50 .map_err(|e| {
51 FieldViolation::new("object_id")
52 .with_description(format!("invalid object_id: {e}"))
53 .with_reason(ErrorReason::FieldInvalid)
54 .nested_at("requests", idx)
55 })?;
56 Ok((object_id, version))
57 })
58 .collect::<Result<_, RpcError>>()?;
59 Ok((requests, read_mask))
60}
61
62#[tracing::instrument(skip(service))]
63pub fn get_object(
64 service: &RpcService,
65 GetObjectRequest {
66 object_id,
67 version,
68 read_mask,
69 ..
70 }: GetObjectRequest,
71) -> Result<GetObjectResponse, RpcError> {
72 let (requests, read_mask) =
73 validate_get_object_requests(vec![(object_id, version)], read_mask)?;
74 let (object_id, version) = requests[0];
75 get_object_impl(service, object_id, version, &read_mask).map(GetObjectResponse::new)
76}
77
78#[tracing::instrument(skip(service))]
79pub fn batch_get_objects(
80 service: &RpcService,
81 BatchGetObjectsRequest {
82 requests,
83 read_mask,
84 ..
85 }: BatchGetObjectsRequest,
86) -> Result<BatchGetObjectsResponse, RpcError> {
87 let requests = requests
88 .into_iter()
89 .map(|req| (req.object_id, req.version))
90 .collect();
91 let (requests, read_mask) = validate_get_object_requests(requests, read_mask)?;
92 let objects = requests
93 .into_iter()
94 .map(|(object_id, version)| get_object_impl(service, object_id, version, &read_mask))
95 .map(|result| match result {
96 Ok(object) => GetObjectResult::new_object(object),
97 Err(error) => GetObjectResult::new_error(error.into_status_proto()),
98 })
99 .collect();
100 Ok(BatchGetObjectsResponse::new(objects))
101}
102
103#[tracing::instrument(skip(service))]
104fn get_object_impl(
105 service: &RpcService,
106 object_id: Address,
107 version: Option<u64>,
108 read_mask: &FieldMaskTree,
109) -> Result<Object, RpcError> {
110 let object = if let Some(version) = version {
111 service
112 .reader
113 .inner()
114 .get_object_by_key(&object_id.into(), version.into())
115 .ok_or_else(|| ObjectNotFoundError::new_with_version(object_id, version))?
116 } else {
117 service
118 .reader
119 .inner()
120 .get_object(&object_id.into())
121 .ok_or_else(|| ObjectNotFoundError::new(object_id))?
122 };
123
124 let mut message = Object::default();
125
126 if read_mask.contains(Object::JSON_FIELD.name) {
127 message.json = crate::grpc::v2::render_object_to_json(service, &object).map(Box::new);
128 }
129
130 message.merge(&object, read_mask);
131
132 Ok(message)
133}