app_store_connect/
profile_api.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use crate::{
8    bundle_api::BundleIdResponse, certs_api::CertificatesResponse, AppStoreConnectClient, Result,
9};
10use serde::{Deserialize, Serialize};
11
12const APPLE_PROFILES_URL: &str = "https://api.appstoreconnect.apple.com/v1/profiles";
13
14impl AppStoreConnectClient {
15    pub fn create_profile(
16        &self,
17        name: &str,
18        profile_type: ProfileType,
19        bundle_id: &str,
20        certificates: &[String],
21        devices: Option<&[String]>,
22    ) -> Result<ProfileResponse> {
23        let token = self.get_token()?;
24        let body = ProfileCreateRequest {
25            data: ProfileCreateRequestData {
26                attributes: ProfileCreateRequestAttributes {
27                    name: name.into(),
28                    profile_type: profile_type.to_string(),
29                },
30                relationships: ProfileCreateRequestRelationships {
31                    bundle_id: Ref {
32                        data: RefData {
33                            id: bundle_id.into(),
34                            r#type: "bundleIds".into(),
35                        },
36                    },
37                    certificates: Refs {
38                        data: certificates
39                            .iter()
40                            .map(|certificate| RefData {
41                                id: certificate.into(),
42                                r#type: "certificates".into(),
43                            })
44                            .collect(),
45                    },
46                    devices: devices.map(|devices| Refs {
47                        data: devices
48                            .iter()
49                            .map(|device| RefData {
50                                id: device.into(),
51                                r#type: "devices".into(),
52                            })
53                            .collect(),
54                    }),
55                },
56                r#type: "profiles".into(),
57            },
58        };
59        let req = self
60            .client
61            .post(APPLE_PROFILES_URL)
62            .bearer_auth(token)
63            .header("Accept", "application/json")
64            .header("Content-Type", "application/json")
65            .json(&body);
66        Ok(self.send_request(req)?.json()?)
67    }
68
69    pub fn get_profile_bundle_id(&self, profile_id: &str) -> Result<BundleIdResponse> {
70        let token = self.get_token()?;
71        let req = self
72            .client
73            .get(format!("{APPLE_PROFILES_URL}/{profile_id}/bundleId"))
74            .bearer_auth(token)
75            .header("Accept", "application/json");
76        Ok(self.send_request(req)?.json()?)
77    }
78
79    pub fn list_profile_certificates(&self, profile_id: &str) -> Result<CertificatesResponse> {
80        let token = self.get_token()?;
81        let req = self
82            .client
83            .get(format!("{APPLE_PROFILES_URL}/{profile_id}/certificates"))
84            .bearer_auth(token)
85            .header("Accept", "application/json");
86        Ok(self.send_request(req)?.json()?)
87    }
88
89    pub fn list_profiles(&self) -> Result<ProfilesResponse> {
90        let token = self.get_token()?;
91        let req = self
92            .client
93            .get(APPLE_PROFILES_URL)
94            .bearer_auth(token)
95            .header("Accept", "application/json");
96        Ok(self.send_request(req)?.json()?)
97    }
98
99    pub fn get_profile(&self, id: &str) -> Result<ProfileResponse> {
100        let token = self.get_token()?;
101        let req = self
102            .client
103            .get(format!("{APPLE_PROFILES_URL}/{id}"))
104            .bearer_auth(token)
105            .header("Accept", "application/json");
106        Ok(self.send_request(req)?.json()?)
107    }
108
109    pub fn delete_profile(&self, id: &str) -> Result<()> {
110        let token = self.get_token()?;
111        let req = self
112            .client
113            .delete(format!("{APPLE_PROFILES_URL}/{id}"))
114            .bearer_auth(token);
115        self.send_request(req)?;
116        Ok(())
117    }
118}
119
120#[derive(Debug, Serialize)]
121#[serde(rename_all = "camelCase")]
122pub struct ProfileCreateRequest {
123    pub data: ProfileCreateRequestData,
124}
125
126#[derive(Debug, Serialize)]
127#[serde(rename_all = "camelCase")]
128pub struct ProfileCreateRequestData {
129    pub attributes: ProfileCreateRequestAttributes,
130    pub relationships: ProfileCreateRequestRelationships,
131    pub r#type: String,
132}
133
134#[derive(Debug, Serialize)]
135#[serde(rename_all = "camelCase")]
136pub struct ProfileCreateRequestAttributes {
137    pub name: String,
138    pub profile_type: String,
139}
140
141#[derive(Debug, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct ProfileCreateRequestRelationships {
144    pub bundle_id: Ref,
145    pub certificates: Refs,
146    pub devices: Option<Refs>,
147}
148
149#[derive(Debug, Serialize)]
150#[serde(rename_all = "camelCase")]
151pub struct Ref {
152    pub data: RefData,
153}
154
155#[derive(Debug, Serialize)]
156#[serde(rename_all = "camelCase")]
157pub struct Refs {
158    pub data: Vec<RefData>,
159}
160
161#[derive(Debug, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct RefData {
164    pub id: String,
165    pub r#type: String,
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq, clap::ValueEnum)]
169pub enum ProfileType {
170    IosAppDevelopment,
171    MacAppDevelopment,
172    IosAppStore,
173    MacAppStore,
174    MacAppDirect,
175}
176
177impl std::fmt::Display for ProfileType {
178    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
179        let s = match self {
180            Self::IosAppDevelopment => "IOS_APP_DEVELOPMENT",
181            Self::MacAppDevelopment => "MAC_APP_DEVELOPMENT",
182            Self::IosAppStore => "IOS_APP_STORE",
183            Self::MacAppStore => "MAC_APP_STORE",
184            Self::MacAppDirect => "MAC_APP_DIRECT",
185        };
186        write!(f, "{s}")
187    }
188}
189
190#[derive(Debug, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct ProfileResponse {
193    pub data: Profile,
194}
195
196#[derive(Debug, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct ProfilesResponse {
199    pub data: Vec<Profile>,
200}
201
202#[derive(Debug, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct Profile {
205    pub attributes: ProfileAttributes,
206    pub id: String,
207}
208
209#[derive(Debug, Deserialize)]
210#[serde(rename_all = "camelCase")]
211pub struct ProfileAttributes {
212    pub name: String,
213    pub platform: String,
214    pub profile_content: String,
215    pub uuid: String,
216    pub created_date: Option<String>,
217    pub profile_state: String,
218    pub profile_type: String,
219    pub expiration_date: String,
220}