main
rs 94 lines 3.33 KB
Raw
1 //! `GET /v1/appPriceSchedules/{id}`.
2 //!
3 //! A price schedule is the container for everything an app costs. There is
4 //! no `POST` here: the schedule comes into existence with the app, and its
5 //! id **is** the app's id — `/v1/appPriceSchedules/6751143237` and
6 //! `/v1/apps/6751143237` describe the same product. Callers therefore pass
7 //! an app id, not a separately-looked-up schedule id.
8 //!
9 //! The resource carries no attributes at all. Its only payload is
10 //! relationships: the base territory, the manual prices, and the automatic
11 //! ones. This module reads the base territory — the storefront whose price
12 //! the developer set by hand, and from which Apple converts every other
13 //! storefront. Reading the prices themselves is [`crate::app_price`].
14
15 use async_trait::async_trait;
16 use reqwest::Method;
17 use serde::{Deserialize, Serialize};
18 use smbcloud_ascapi_core::Client;
19 use smbcloud_ascapi_core::Result;
20
21 pub const RESOURCE_TYPE: &str = "appPriceSchedules";
22
23 /// An app's price schedule, flattened to the one fact it carries.
24 ///
25 /// Not [`Resource`](smbcloud_ascapi_core::jsonapi::Resource), because that
26 /// type requires an `attributes` object and `appPriceSchedules` returns
27 /// none — the resource is relationships and links only.
28 #[derive(Debug, Clone, Serialize, Deserialize)]
29 #[serde(rename_all = "camelCase")]
30 pub struct AppPriceSchedule {
31 /// The schedule id, which is also the app's App Store Connect id.
32 pub id: String,
33 /// Territory code whose price was set manually and from which Apple
34 /// converts the rest, e.g. `USA`. `None` if Apple did not return the
35 /// relationship.
36 pub base_territory: Option<String>,
37 }
38
39 #[derive(Debug, Clone, Deserialize)]
40 struct ScheduleDocument {
41 data: ScheduleResource,
42 }
43
44 #[derive(Debug, Clone, Deserialize)]
45 struct ScheduleResource {
46 id: String,
47 #[serde(default)]
48 relationships: ScheduleRelationships,
49 }
50
51 #[derive(Debug, Clone, Default, Deserialize)]
52 #[serde(rename_all = "camelCase")]
53 struct ScheduleRelationships {
54 #[serde(default)]
55 base_territory: Option<crate::app_price::ToOne>,
56 }
57
58 /// App price schedules: the container an app's prices hang off.
59 ///
60 /// An extension trait rather than inherent methods, because `Client`
61 /// lives in the core crate and Rust only allows inherent impls in the
62 /// crate that defines the type. Import it, or the crate's `prelude`,
63 /// to call these on a `Client`.
64 #[async_trait]
65 pub trait AppPriceSchedulesApi {
66 /// `GET /v1/appPriceSchedules/{app_id}` — the app's base territory.
67 ///
68 /// Takes the **app** id; schedule and app share one id.
69 async fn get_app_price_schedule(&self, app_id: &str) -> Result<AppPriceSchedule>;
70 }
71
72 #[async_trait]
73 impl AppPriceSchedulesApi for Client {
74 async fn get_app_price_schedule(&self, app_id: &str) -> Result<AppPriceSchedule> {
75 let path = format!("/v1/appPriceSchedules/{app_id}");
76 let doc: ScheduleDocument = self
77 .request(
78 Method::GET,
79 &path,
80 &[("include", "baseTerritory")],
81 None::<&()>,
82 )
83 .await?;
84 Ok(AppPriceSchedule {
85 id: doc.data.id,
86 base_territory: doc
87 .data
88 .relationships
89 .base_territory
90 .and_then(|rel| rel.data)
91 .map(|reference| reference.id),
92 })
93 }
94 }