apple_search_ads/endpoints/
get_ad_group_level_reports.rs1use 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#[derive(Debug)]
30pub struct CustomizableGetAdGroupLevelReports<M, I> {
31 pub org_id: u64,
32 pub campaign_id: u64,
33 pub reporting_request: ReportingRequest,
34 pub access_token: Box<str>,
36 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
116pub 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}