mangadex_api/v5/report/
get.rs1use derive_builder::Builder;
27use serde::Serialize;
28
29use crate::HttpClientRef;
30use mangadex_api_schema::v5::UserReportsCollection;
31use mangadex_api_types::{
32 ReferenceExpansionResource, ReportCategory, ReportSortOrder, ReportStatus,
33};
34use uuid::Uuid;
35
36#[cfg_attr(
37 feature = "deserializable-endpoint",
38 derive(serde::Deserialize, getset::Getters, getset::Setters)
39)]
40#[derive(Debug, Serialize, Clone, Builder, Default)]
41#[serde(rename_all = "camelCase")]
42#[builder(
43 setter(into, strip_option),
44 default,
45 build_fn(error = "mangadex_api_types::error::BuilderError")
46)]
47pub struct ListReportsByUser {
48 #[doc(hidden)]
49 #[serde(skip)]
50 #[builder(pattern = "immutable")]
51 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
52 pub http_client: HttpClientRef,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub limit: Option<u32>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub offset: Option<u32>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub category: Option<ReportCategory>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub object_id: Option<Uuid>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub reason_id: Option<Uuid>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub status: Option<ReportStatus>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub order: Option<ReportSortOrder>,
68 #[builder(setter(each = "include"))]
69 #[serde(skip_serializing_if = "Vec::is_empty")]
70 pub includes: Vec<ReferenceExpansionResource>,
71}
72
73endpoint! {
74 GET "/report",
75 #[query auth] ListReportsByUser,
76 #[rate_limited] UserReportsCollection,
77 ListReportsByUserBuilder
78}
79
80#[cfg(test)]
81mod tests {
82 use serde_json::json;
83 use time::OffsetDateTime;
84 use url::Url;
85 use uuid::Uuid;
86 use wiremock::matchers::{method, path};
87 use wiremock::{Mock, MockServer, ResponseTemplate};
88
89 use crate::v5::AuthTokens;
90 use crate::{HttpClient, MangaDexClient};
91 use mangadex_api_types::{MangaDexDateTime, ReportCategory, ReportStatus, ResponseType};
92
93 #[tokio::test]
94 async fn list_reports_by_user_fires_a_request_to_base_url() -> anyhow::Result<()> {
95 let mock_server = MockServer::start().await;
96 let http_client = HttpClient::builder()
97 .base_url(Url::parse(&mock_server.uri())?)
98 .auth_tokens(AuthTokens {
99 session: "sessiontoken".to_string(),
100 refresh: "refreshtoken".to_string(),
101 })
102 .build()?;
103 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
104
105 let report_id = Uuid::new_v4();
106 let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
107
108 let response_body = json!({
109 "result": "ok",
110 "response": "collection",
111 "data": [
112 {
113 "id": report_id,
114 "type": "report",
115 "attributes": {
116 "details": "The manga was a troll submission.",
117 "objectId": "2",
118 "status": "accepted",
119 "createdAt": datetime.to_string()
120 },
121 "relationships": []
122 }
123 ],
124 "limit": 10,
125 "offset": 0,
126 "total": 1
127 });
128
129 Mock::given(method("GET"))
130 .and(path("/report"))
131 .respond_with(
132 ResponseTemplate::new(200)
133 .insert_header("x-ratelimit-retry-after", "1698723860")
134 .insert_header("x-ratelimit-limit", "40")
135 .insert_header("x-ratelimit-remaining", "39")
136 .set_body_json(response_body),
137 )
138 .expect(1)
139 .mount(&mock_server)
140 .await;
141
142 let res = mangadex_client
143 .report()
144 .get()
145 .category(ReportCategory::Manga)
146 .send()
147 .await?;
148
149 assert_eq!(res.response, ResponseType::Collection);
150 assert_eq!(res.limit, 10);
151 assert_eq!(res.offset, 0);
152 assert_eq!(res.total, 1);
153 let report = &res.data[0];
154 assert_eq!(report.id, report_id);
155
156 assert_eq!(
157 report.attributes.details,
158 "The manga was a troll submission.".to_string()
159 );
160 assert_eq!(report.attributes.object_id, "2".to_string());
161 assert_eq!(report.attributes.status, ReportStatus::Accepted);
162
163 Ok(())
164 }
165}