apple_search_ads/endpoints/
get_ad_group_level_reports.rs

1// https://developer.apple.com/documentation/apple_search_ads/get__ad_group-level_reports
2
3use core::marker::PhantomData;
4
5use http_api_client_endpoint::{
6    http::{
7        header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT},
8        Method, StatusCode,
9    },
10    Body, Endpoint, Request, Response, MIME_APPLICATION_JSON,
11};
12use serde::de::DeserializeOwned;
13use url::Url;
14
15use crate::{
16    endpoints::{
17        common::{EndpointError, EndpointRet},
18        HEADER_KEY_X_AP_CONTEXT,
19    },
20    objects::{
21        reporting_request::ReportingRequest, reporting_response_body::ReportingResponseBody,
22        row::AdGroupLevelRowMetaData,
23    },
24};
25
26pub const URL: &str = "https://api.searchads.apple.com/api/v4/reports/campaigns/{}/adgroups";
27
28//
29#[derive(Debug)]
30pub struct CustomizableGetAdGroupLevelReports<M, I> {
31    pub org_id: u64,
32    pub campaign_id: u64,
33    pub reporting_request: ReportingRequest,
34    //
35    pub access_token: Box<str>,
36    //
37    phantom_m: PhantomData<M>,
38    phantom_i: PhantomData<I>,
39}
40impl<M, I> CustomizableGetAdGroupLevelReports<M, I> {
41    pub fn new(
42        org_id: u64,
43        campaign_id: u64,
44        reporting_request: ReportingRequest,
45        access_token: impl AsRef<str>,
46    ) -> Self {
47        Self {
48            org_id,
49            campaign_id,
50            reporting_request,
51            access_token: access_token.as_ref().into(),
52            phantom_m: PhantomData,
53            phantom_i: PhantomData,
54        }
55    }
56}
57
58impl<M, I> Endpoint for CustomizableGetAdGroupLevelReports<M, I>
59where
60    M: DeserializeOwned,
61    I: DeserializeOwned,
62{
63    type RenderRequestError = EndpointError;
64
65    type ParseResponseOutput = EndpointRet<ReportingResponseBody<M, I>>;
66    type ParseResponseError = EndpointError;
67
68    fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
69        let url = Url::parse(
70            format!(
71                "https://api.searchads.apple.com/api/v4/reports/campaigns/{}/adgroups",
72                self.campaign_id
73            )
74            .as_str(),
75        )
76        .map_err(EndpointError::MakeRequestUrlFailed)?;
77
78        let body = serde_json::to_vec(&self.reporting_request)
79            .map_err(EndpointError::SerRequestBodyJsonFailed)?;
80
81        let request = Request::builder()
82            .method(Method::POST)
83            .uri(url.as_str())
84            .header(USER_AGENT, "apple-search-ads")
85            .header(ACCEPT, MIME_APPLICATION_JSON)
86            .header(CONTENT_TYPE, MIME_APPLICATION_JSON)
87            .header(AUTHORIZATION, format!("Bearer {}", self.access_token))
88            .header(HEADER_KEY_X_AP_CONTEXT, format!("orgId={}", self.org_id))
89            .body(body)
90            .map_err(EndpointError::MakeRequestFailed)?;
91
92        Ok(request)
93    }
94
95    fn parse_response(
96        &self,
97        response: Response<Body>,
98    ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
99        let status = response.status();
100        match status {
101            StatusCode::OK => Ok(EndpointRet::Ok(
102                serde_json::from_slice(response.body())
103                    .map_err(EndpointError::DeResponseBodyOkJsonFailed)?,
104            )),
105            status => match serde_json::from_slice(response.body()) {
106                Ok(err_json) => Ok(EndpointRet::Other((status, Ok(err_json)))),
107                Err(_) => Ok(EndpointRet::Other((
108                    status,
109                    Err(response.body().to_owned()),
110                ))),
111            },
112        }
113    }
114}
115
116//
117//
118//
119pub type GetAdGroupLevelReports = CustomizableGetAdGroupLevelReports<AdGroupLevelRowMetaData, ()>;
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_render_request() {
127        let ep = GetAdGroupLevelReports::new(1, 2, Default::default(), "TOKEN");
128        let req = ep.render_request().unwrap();
129        assert_eq!(req.method(), "POST");
130        assert_eq!(
131            req.uri(),
132            "https://api.searchads.apple.com/api/v4/reports/campaigns/2/adgroups"
133        );
134        assert_eq!(req.headers().get("Authorization").unwrap(), "Bearer TOKEN");
135        assert_eq!(req.headers().get("X-AP-Context").unwrap(), "orgId=1");
136        assert!(!req.body().is_empty());
137    }
138}