amazon_spapi/client_apis/
reports_api.rs

1use std::time::Duration;
2
3use crate::{
4    client::{ApiEndpoint, ApiMethod, SpapiClient},
5    models::{
6        self,
7        reports_2021_06_30::{report::ProcessingStatus, CreateReportSpecification},
8        sellers::{GetAccountResponse, GetMarketplaceParticipationsResponse},
9    },
10};
11use anyhow::Result;
12use tokio::time::sleep;
13
14impl SpapiClient {
15    pub async fn cancel_report(&self, report_id: &str) -> Result<()> {
16        let configuration = self.create_configuration().await?;
17        let guard = self
18            .limiter()
19            .wait("/reports/v2021-06-30/reports/{reportId}/cancel", 0.016, 15)
20            .await?;
21        let res = crate::apis::reports_api::cancel_report(&configuration, report_id).await?;
22        guard.mark_response().await;
23        Ok(res)
24    }
25
26    pub async fn cancel_report_schedule(&self, report_schedule_id: &str) -> Result<()> {
27        let configuration = self.create_configuration().await?;
28        let guard = self
29            .limiter()
30            .wait(
31                "/reports/v2021-06-30/schedules/{reportScheduleId}/cancel",
32                0.016,
33                15,
34            )
35            .await?;
36        let res =
37            crate::apis::reports_api::cancel_report_schedule(&configuration, report_schedule_id)
38                .await?;
39        guard.mark_response().await;
40        Ok(res)
41    }
42
43    pub async fn create_report(
44        &self,
45        body: models::reports_2021_06_30::CreateReportSpecification,
46    ) -> Result<models::reports_2021_06_30::CreateReportResponse> {
47        let configuration = self.create_configuration().await?;
48        let guard = self
49            .limiter()
50            .wait("/reports/v2021-06-30/reports", 0.016, 15)
51            .await?;
52        let res = crate::apis::reports_api::create_report(&configuration, body).await?;
53        guard.mark_response().await;
54        Ok(res)
55    }
56
57    pub async fn create_report_schedule(
58        &self,
59        body: models::reports_2021_06_30::CreateReportScheduleSpecification,
60    ) -> Result<models::reports_2021_06_30::CreateReportScheduleResponse> {
61        let configuration = self.create_configuration().await?;
62        let guard = self
63            .limiter()
64            .wait("/reports/v2021-06-30/schedules", 0.016, 15)
65            .await?;
66        let res = crate::apis::reports_api::create_report_schedule(&configuration, body).await?;
67        guard.mark_response().await;
68        Ok(res)
69    }
70
71    pub async fn get_report(&self, report_id: &str) -> Result<models::reports_2021_06_30::Report> {
72        let configuration = self.create_configuration().await?;
73        let guard = self
74            .limiter()
75            .wait("/reports/v2021-06-30/reports/{reportId}", 0.016, 15)
76            .await?;
77        let res = crate::apis::reports_api::get_report(&configuration, report_id).await?;
78        guard.mark_response().await;
79        Ok(res)
80    }
81
82    pub async fn get_report_document(
83        &self,
84        report_document_id: &str,
85    ) -> Result<models::reports_2021_06_30::ReportDocument> {
86        let configuration = self.create_configuration().await?;
87        let guard = self
88            .limiter()
89            .wait(
90                "/reports/v2021-06-30/reports/{reportId}/document",
91                0.016,
92                15,
93            )
94            .await?;
95        let res = crate::apis::reports_api::get_report_document(&configuration, report_document_id)
96            .await?;
97        guard.mark_response().await;
98        Ok(res)
99    }
100
101    pub async fn get_report_schedule(
102        &self,
103        report_schedule_id: &str,
104    ) -> Result<models::reports_2021_06_30::ReportSchedule> {
105        let configuration = self.create_configuration().await?;
106        let guard = self
107            .limiter()
108            .wait(
109                "/reports/v2021-06-30/schedules/{reportScheduleId}",
110                0.016,
111                15,
112            )
113            .await?;
114        let res =
115            crate::apis::reports_api::get_report_schedule(&configuration, report_schedule_id).await?;
116        guard.mark_response().await;
117        Ok(res)
118    }
119
120    pub async fn get_report_schedules(
121        &self,
122        report_types: Vec<String>,
123    ) -> Result<models::reports_2021_06_30::ReportScheduleList> {
124        let configuration = self.create_configuration().await?;
125        let guard = self
126            .limiter()
127            .wait("/reports/v2021-06-30/schedules", 0.016, 15)
128            .await?;
129        let res =
130            crate::apis::reports_api::get_report_schedules(&configuration, report_types).await?;
131        guard.mark_response().await;
132        Ok(res)
133    }
134
135    pub async fn get_reports(
136        &self,
137        report_types: Option<Vec<String>>,
138        processing_statuses: Option<Vec<String>>,
139        marketplace_ids: Option<Vec<String>>,
140        page_size: Option<i32>,
141        created_since: Option<String>,
142        created_until: Option<String>,
143        next_token: Option<&str>,
144    ) -> Result<models::reports_2021_06_30::GetReportsResponse> {
145        let configuration = self.create_configuration().await?;
146        let guard = self
147            .limiter()
148            .wait("/reports/v2021-06-30/reports", 0.016, 15)
149            .await?;
150        let res = crate::apis::reports_api::get_reports(
151            &configuration,
152            report_types,
153            processing_statuses,
154            marketplace_ids,
155            page_size,
156            created_since,
157            created_until,
158            next_token,
159        )
160        .await?;
161        guard.mark_response().await;
162        Ok(res)
163    }
164
165    /// Convenience method to fetch a report by type and marketplace IDs, defaulting to a 30-minute wait.
166    pub async fn fetch_report(
167        &self,
168        report_type: &str,
169        marketplace_ids: Vec<String>,
170        max_wait_minutes: Option<u32>,
171        progress_callback: Option<impl Fn(u32, ProcessingStatus) + Send + 'static>,
172    ) -> Result<String> {
173        let max_wait_minutes = max_wait_minutes.unwrap_or(30);
174        let check_interval_seconds = 10;
175        let max_attempts = (max_wait_minutes * 60 / check_interval_seconds) as u32;
176
177        let create_report_spec = CreateReportSpecification {
178            report_type: report_type.to_string(),
179            marketplace_ids,
180            report_options: None,
181            data_start_time: None,
182            data_end_time: None,
183        };
184
185        let create_response = self.create_report(create_report_spec).await?;
186        let report_id = &create_response.report_id;
187
188        for attempt_count in 1..=max_attempts {
189            let report = self.get_report(report_id).await?;
190
191            if let Some(callback) = &progress_callback {
192                callback(attempt_count, report.processing_status.clone());
193            }
194
195            match report.processing_status {
196                ProcessingStatus::Done => {
197                    log::debug!("Report generation completed!");
198
199                    if let Some(document_id) = report.report_document_id {
200                        let document = self.get_report_document(&document_id).await?;
201                        log::info!("Report document ID: {}", document.report_document_id);
202
203                        let report_content =
204                            if let Some(compression) = &document.compression_algorithm {
205                                log::info!("Report uses compression algorithm: {:?}", compression);
206                                // TODO: decompress the report content
207                                self.download(&document.url).await?
208                            } else {
209                                self.download(&document.url).await?
210                            };
211
212                        log::debug!("Report content downloaded successfully.");
213                        return Ok(report_content);
214                    } else {
215                        return Err(anyhow::anyhow!(
216                            "Report generation completed but no document ID found"
217                        ));
218                    }
219                }
220                ProcessingStatus::Cancelled => {
221                    return Err(anyhow::anyhow!("Report generation was cancelled"));
222                }
223                ProcessingStatus::Fatal => {
224                    return Err(anyhow::anyhow!("Report generation failed"));
225                }
226                ProcessingStatus::InProgress => {
227                    log::debug!("Report is being generated, waiting 30 seconds before retrying...");
228                }
229                ProcessingStatus::InQueue => {
230                    log::debug!("Report is in queue waiting to be processed, waiting 30 seconds before retrying...");
231                }
232            }
233
234            if attempt_count < max_attempts {
235                log::debug!(
236                    "Waiting {} seconds before next check...",
237                    check_interval_seconds
238                );
239                sleep(Duration::from_secs(check_interval_seconds as u64)).await;
240            }
241        }
242
243        Err(anyhow::anyhow!(
244            "Report generation timed out after {} minutes ({} attempts)",
245            max_wait_minutes,
246            max_attempts
247        ))
248    }
249}