development
rs 92 lines 2.76 KB
Raw
1 //! Sales report downloads from App Store Connect.
2 //!
3 //! Apple returns these reports as gzip-compressed TSV rather than JSON:API.
4 //! This crate owns the request shape; consumers decide how to persist or
5 //! transform the downloaded file.
6
7 use async_trait::async_trait;
8 use reqwest::Method;
9 use smbcloud_ascapi_core::{Client, Result};
10
11 /// Arguments accepted by `GET /v1/salesReports`.
12 #[derive(Debug, Clone)]
13 pub struct SalesReportRequest {
14 pub vendor_number: String,
15 pub frequency: String,
16 pub report_date: String,
17 pub report_type: String,
18 pub report_subtype: String,
19 pub version: Option<String>,
20 }
21
22 impl SalesReportRequest {
23 /// Build the documented App Store Connect query filters.
24 pub fn query(&self) -> Vec<(&str, &str)> {
25 let mut query = vec![
26 ("filter[frequency]", self.frequency.as_str()),
27 ("filter[reportDate]", self.report_date.as_str()),
28 ("filter[reportSubType]", self.report_subtype.as_str()),
29 ("filter[reportType]", self.report_type.as_str()),
30 ("filter[vendorNumber]", self.vendor_number.as_str()),
31 ];
32 if let Some(version) = self.version.as_deref() {
33 query.push(("filter[version]", version));
34 }
35 query
36 }
37 }
38
39 /// Download account-level sales data. The successful body is Apple’s
40 /// gzip-compressed TSV report.
41 #[async_trait]
42 pub trait SalesReportsApi {
43 async fn download_sales_report(&self, request: &SalesReportRequest) -> Result<Vec<u8>>;
44 }
45
46 #[async_trait]
47 impl SalesReportsApi for Client {
48 async fn download_sales_report(&self, request: &SalesReportRequest) -> Result<Vec<u8>> {
49 self.request_bytes(
50 Method::GET,
51 "/v1/salesReports",
52 &request.query(),
53 None::<&()>,
54 &[("Accept", "application/a-gzip")],
55 )
56 .await
57 }
58 }
59
60 /// Traits to import when using this crate.
61 pub mod prelude {
62 pub use crate::SalesReportsApi;
63 }
64
65 #[cfg(test)]
66 mod tests {
67 use super::SalesReportRequest;
68
69 #[test]
70 fn query_contains_defaults_and_optional_version() {
71 let request = SalesReportRequest {
72 vendor_number: "12345678".into(),
73 frequency: "DAILY".into(),
74 report_date: "2026-09-11".into(),
75 report_type: "SALES".into(),
76 report_subtype: "SUMMARY".into(),
77 version: Some("1_0".into()),
78 };
79
80 assert_eq!(
81 request.query(),
82 vec![
83 ("filter[frequency]", "DAILY"),
84 ("filter[reportDate]", "2026-09-11"),
85 ("filter[reportSubType]", "SUMMARY"),
86 ("filter[reportType]", "SALES"),
87 ("filter[vendorNumber]", "12345678"),
88 ("filter[version]", "1_0"),
89 ]
90 );
91 }
92 }