iota_rest_api/
accounts.rs1use axum::extract::{Path, Query, State};
6use iota_sdk2::types::{Address, ObjectId, StructTag, Version};
7use iota_types::iota_sdk2_conversions::struct_tag_core_to_sdk;
8use openapiv3::v3_1::Operation;
9use tap::Pipe;
10
11use crate::{
12 Page, RestService, Result,
13 openapi::{ApiEndpoint, OperationBuilder, ResponseBuilder, RouteHandler},
14 reader::StateReader,
15 response::ResponseContent,
16};
17
18pub struct ListAccountObjects;
19
20impl ApiEndpoint<RestService> for ListAccountObjects {
21 fn method(&self) -> axum::http::Method {
22 axum::http::Method::GET
23 }
24
25 fn path(&self) -> &'static str {
26 "/accounts/{account}/objects"
27 }
28
29 fn operation(&self, generator: &mut schemars::gen::SchemaGenerator) -> Operation {
30 OperationBuilder::new()
31 .tag("Account")
32 .operation_id("ListAccountObjects")
33 .path_parameter::<Address>("account", generator)
34 .query_parameters::<ListAccountOwnedObjectsQueryParameters>(generator)
35 .response(
36 200,
37 ResponseBuilder::new()
38 .json_content::<Vec<AccountOwnedObjectInfo>>(generator)
39 .header::<String>(crate::types::X_IOTA_CURSOR, generator)
40 .build(),
41 )
42 .build()
43 }
44
45 fn handler(&self) -> crate::openapi::RouteHandler<RestService> {
46 RouteHandler::new(self.method(), list_account_objects)
47 }
48}
49
50async fn list_account_objects(
51 Path(address): Path<Address>,
52 Query(parameters): Query<ListAccountOwnedObjectsQueryParameters>,
53 State(state): State<StateReader>,
54) -> Result<Page<AccountOwnedObjectInfo, ObjectId>> {
55 let limit = parameters.limit();
56 let start = parameters.start();
57
58 let mut object_info = state
59 .inner()
60 .account_owned_objects_info_iter(address.into(), start)?
61 .take(limit + 1)
62 .map(|info| {
63 AccountOwnedObjectInfo {
64 owner: info.owner.into(),
65 object_id: info.object_id.into(),
66 version: info.version.into(),
67 type_: struct_tag_core_to_sdk(info.type_.into())?,
68 }
69 .pipe(Ok)
70 })
71 .collect::<Result<Vec<_>>>()?;
72
73 let cursor = if object_info.len() > limit {
74 object_info.pop().unwrap().object_id.pipe(Some)
77 } else {
78 None
79 };
80
81 object_info
82 .pipe(ResponseContent::Json)
83 .pipe(|entries| Page { entries, cursor })
84 .pipe(Ok)
85}
86
87#[derive(Debug, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
88pub struct ListAccountOwnedObjectsQueryParameters {
89 pub limit: Option<u32>,
90 pub start: Option<ObjectId>,
91}
92
93impl ListAccountOwnedObjectsQueryParameters {
94 pub fn limit(&self) -> usize {
95 self.limit
96 .map(|l| (l as usize).clamp(1, crate::MAX_PAGE_SIZE))
97 .unwrap_or(crate::DEFAULT_PAGE_SIZE)
98 }
99
100 pub fn start(&self) -> Option<iota_types::base_types::ObjectID> {
101 self.start.map(Into::into)
102 }
103}
104
105#[serde_with::serde_as]
106#[derive(Debug, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
107pub struct AccountOwnedObjectInfo {
108 pub owner: Address,
109 pub object_id: ObjectId,
110 #[serde_as(as = "iota_types::iota_serde::BigInt<u64>")]
111 #[schemars(with = "crate::_schemars::U64")]
112 pub version: Version,
113 #[serde(rename = "type")]
114 pub type_: StructTag,
115}