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