mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 06:26:16 +01:00
Improve developer session layout
This commit is contained in:
@@ -59,9 +59,9 @@ async fn main() {
|
|||||||
.expect("No developer teams available for this account");
|
.expect("No developer teams available for this account");
|
||||||
|
|
||||||
let res = dev_session
|
let res = dev_session
|
||||||
.list_all_development_certs(team, None)
|
.list_devices(team, None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to list developer devices");
|
.expect("Failed to list developer devices");
|
||||||
|
|
||||||
println!("{:#?}", res);
|
println!("{:?}", res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use plist::Dictionary;
|
|||||||
use plist_macro::{plist, plist_to_xml_string};
|
use plist_macro::{plist, plist_to_xml_string};
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use tracing::warn;
|
use tracing::{error, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -12,10 +12,8 @@ use crate::{
|
|||||||
grandslam::GrandSlam,
|
grandslam::GrandSlam,
|
||||||
},
|
},
|
||||||
dev::structures::{
|
dev::structures::{
|
||||||
DeveloperDevice,
|
|
||||||
DeveloperDeviceType::{self, *},
|
DeveloperDeviceType::{self, *},
|
||||||
DeveloperTeam, DevelopmentCertificate, ListCertificatesResponse, ListDevicesResponse,
|
*,
|
||||||
ListTeamsResponse,
|
|
||||||
},
|
},
|
||||||
util::plist::PlistDataExtract,
|
util::plist::PlistDataExtract,
|
||||||
};
|
};
|
||||||
@@ -64,6 +62,7 @@ impl<'a> DeveloperSession<'a> {
|
|||||||
pub async fn send_developer_request<T: DeserializeOwned>(
|
pub async fn send_developer_request<T: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
url: &str,
|
url: &str,
|
||||||
|
result_key: &str,
|
||||||
body: impl Into<Option<Dictionary>>,
|
body: impl Into<Option<Dictionary>>,
|
||||||
) -> Result<T, Report> {
|
) -> Result<T, Report> {
|
||||||
let body = body.into().unwrap_or_else(|| Dictionary::new());
|
let body = body.into().unwrap_or_else(|| Dictionary::new());
|
||||||
@@ -92,26 +91,51 @@ impl<'a> DeveloperSession<'a> {
|
|||||||
.await
|
.await
|
||||||
.context("Failed to read developer request response text")?;
|
.context("Failed to read developer request response text")?;
|
||||||
|
|
||||||
let dict: T = plist::from_bytes(text.as_bytes())
|
let dict: Dictionary = plist::from_bytes(text.as_bytes())
|
||||||
.context("Failed to parse developer request plist")?;
|
.context("Failed to parse developer request plist")?;
|
||||||
|
|
||||||
Ok(dict)
|
// All this error handling is here to ensure that:
|
||||||
|
// 1. We always warn/log errors from the server even if it returns the expected data
|
||||||
|
// 2. We return server errors if the expected data is missing
|
||||||
|
// 3. We return parsing errors if there is no server error but the expected data is missing
|
||||||
|
let response_code = dict.get("resultCode").and_then(|v| v.as_signed_integer());
|
||||||
|
let mut server_error: Option<String> = None;
|
||||||
|
if let Some(code) = response_code {
|
||||||
|
if code != 0 {
|
||||||
|
server_error = Some(format!(
|
||||||
|
"{} Code {}: {}",
|
||||||
|
dict.get("userString")
|
||||||
|
.and_then(|v| v.as_string())
|
||||||
|
.unwrap_or("Developer request failed."),
|
||||||
|
code,
|
||||||
|
dict.get("resultString")
|
||||||
|
.and_then(|v| v.as_string())
|
||||||
|
.unwrap_or("No error message given.")
|
||||||
|
));
|
||||||
|
error!(server_error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("No resultCode in developer request response");
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: Result<T, _> = dict.get_struct(result_key);
|
||||||
|
|
||||||
|
if let Err(_) = &result {
|
||||||
|
if let Some(err) = server_error {
|
||||||
|
bail!(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result.context("Failed to extract developer request result")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_teams(&self) -> Result<Vec<DeveloperTeam>, Report> {
|
pub async fn list_teams(&self) -> Result<Vec<DeveloperTeam>, Report> {
|
||||||
let response: ListTeamsResponse = self
|
let response: Vec<DeveloperTeam> = self
|
||||||
.send_developer_request(&dev_url("listTeams", Any), None)
|
.send_developer_request(&dev_url("listTeams", Any), "teams", None)
|
||||||
.await
|
.await
|
||||||
.context("Failed to list developer teams")?;
|
.context("Failed to list developer teams")?;
|
||||||
|
|
||||||
if response.result_code != 0 {
|
Ok(response)
|
||||||
warn!(
|
|
||||||
"Non-zero list teams response code: {}",
|
|
||||||
response.result_code
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response.teams)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_devices(
|
pub async fn list_devices(
|
||||||
@@ -123,19 +147,33 @@ impl<'a> DeveloperSession<'a> {
|
|||||||
"teamId": &team.team_id,
|
"teamId": &team.team_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
let response: ListDevicesResponse = self
|
let devices: Vec<DeveloperDevice> = self
|
||||||
.send_developer_request(&dev_url("listDevices", device_type), body)
|
.send_developer_request(&dev_url("listDevices", device_type), "devices", body)
|
||||||
.await
|
.await
|
||||||
.context("Failed to list developer devices")?;
|
.context("Failed to list developer devices")?;
|
||||||
|
|
||||||
if response.result_code != 0 {
|
Ok(devices)
|
||||||
warn!(
|
}
|
||||||
"Non-zero list devices response code: {}",
|
|
||||||
response.result_code
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response.devices)
|
pub async fn add_device(
|
||||||
|
&self,
|
||||||
|
team: &DeveloperTeam,
|
||||||
|
name: &str,
|
||||||
|
udid: &str,
|
||||||
|
device_type: impl Into<Option<DeveloperDeviceType>>,
|
||||||
|
) -> Result<DeveloperDevice, Report> {
|
||||||
|
let body = plist!(dict {
|
||||||
|
"teamId": &team.team_id,
|
||||||
|
"name": name,
|
||||||
|
"deviceNumber": udid,
|
||||||
|
});
|
||||||
|
|
||||||
|
let device: DeveloperDevice = self
|
||||||
|
.send_developer_request(&dev_url("addDevice", device_type), "device", body)
|
||||||
|
.await
|
||||||
|
.context("Failed to add developer device")?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_all_development_certs(
|
pub async fn list_all_development_certs(
|
||||||
@@ -147,19 +185,16 @@ impl<'a> DeveloperSession<'a> {
|
|||||||
"teamId": &team.team_id,
|
"teamId": &team.team_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
let response: ListCertificatesResponse = self
|
let certs: Vec<DevelopmentCertificate> = self
|
||||||
.send_developer_request(&dev_url("listAllDevelopmentCerts", device_type), body)
|
.send_developer_request(
|
||||||
|
&dev_url("listAllDevelopmentCerts", device_type),
|
||||||
|
"certificates",
|
||||||
|
body,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.context("Failed to list development certificates")?;
|
.context("Failed to list development certificates")?;
|
||||||
|
|
||||||
if response.result_code != 0 {
|
Ok(certs)
|
||||||
warn!(
|
|
||||||
"Non-zero list development certs response code: {}",
|
|
||||||
response.result_code
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response.certificates)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub struct DeveloperTeam {
|
|||||||
pub struct ListTeamsResponse {
|
pub struct ListTeamsResponse {
|
||||||
pub teams: Vec<DeveloperTeam>,
|
pub teams: Vec<DeveloperTeam>,
|
||||||
pub result_code: i64,
|
pub result_code: i64,
|
||||||
|
pub result_string: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
@@ -45,14 +46,7 @@ pub struct DeveloperDevice {
|
|||||||
pub status: Option<String>,
|
pub status: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ListDevicesResponse {
|
|
||||||
pub devices: Vec<DeveloperDevice>,
|
|
||||||
pub result_code: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DevelopmentCertificate {
|
pub struct DevelopmentCertificate {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -62,9 +56,22 @@ pub struct DevelopmentCertificate {
|
|||||||
pub cert_content: Option<ByteBuf>,
|
pub cert_content: Option<ByteBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
// the automatic debug implementation spams the console with the cert content bytes
|
||||||
#[serde(rename_all = "camelCase")]
|
impl std::fmt::Debug for DevelopmentCertificate {
|
||||||
pub struct ListCertificatesResponse {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
pub certificates: Vec<DevelopmentCertificate>,
|
f.debug_struct("DevelopmentCertificate")
|
||||||
pub result_code: i64,
|
.field("name", &self.name)
|
||||||
|
.field("certificate_id", &self.certificate_id)
|
||||||
|
.field("serial_number", &self.serial_number)
|
||||||
|
.field("machine_id", &self.machine_id)
|
||||||
|
.field(
|
||||||
|
"cert_content",
|
||||||
|
&self
|
||||||
|
.cert_content
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| format!("Some([{} bytes])", c.len()))
|
||||||
|
.unwrap_or("None".to_string()),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use plist::Dictionary;
|
use plist::Dictionary;
|
||||||
use plist_macro::pretty_print_dictionary;
|
use plist_macro::pretty_print_dictionary;
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
pub struct SensitivePlistAttachment {
|
pub struct SensitivePlistAttachment {
|
||||||
pub plist: Dictionary,
|
pub plist: Dictionary,
|
||||||
@@ -11,6 +13,18 @@ impl SensitivePlistAttachment {
|
|||||||
SensitivePlistAttachment { plist }
|
SensitivePlistAttachment { plist }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_text(text: &str) -> Self {
|
||||||
|
let dict: Result<Dictionary, _> = plist::from_bytes(text.as_bytes());
|
||||||
|
if let Err(e) = &dict {
|
||||||
|
error!(
|
||||||
|
"Failed to parse plist text for sensitive attachment, returning empty plist: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return SensitivePlistAttachment::new(Dictionary::new());
|
||||||
|
}
|
||||||
|
SensitivePlistAttachment::new(dict.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
// if env variable DEBUG_SENSITIVE is set, print full plist
|
// if env variable DEBUG_SENSITIVE is set, print full plist
|
||||||
if std::env::var("DEBUG_SENSITIVE").is_ok() {
|
if std::env::var("DEBUG_SENSITIVE").is_ok() {
|
||||||
@@ -41,6 +55,7 @@ pub trait PlistDataExtract {
|
|||||||
fn get_string(&self, key: &str) -> Result<String, Report>;
|
fn get_string(&self, key: &str) -> Result<String, Report>;
|
||||||
fn get_signed_integer(&self, key: &str) -> Result<i64, Report>;
|
fn get_signed_integer(&self, key: &str) -> Result<i64, Report>;
|
||||||
fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>;
|
fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>;
|
||||||
|
fn get_struct<T: DeserializeOwned>(&self, key: &str) -> Result<T, Report>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlistDataExtract for Dictionary {
|
impl PlistDataExtract for Dictionary {
|
||||||
@@ -85,4 +100,24 @@ impl PlistDataExtract for Dictionary {
|
|||||||
.attach(SensitivePlistAttachment::new(self.clone()))
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_struct<T: DeserializeOwned>(&self, key: &str) -> Result<T, Report> {
|
||||||
|
let dict = self.get(key);
|
||||||
|
if dict.is_none() {
|
||||||
|
return Err(report!("Plist missing dictionary for key '{}'", key)
|
||||||
|
.attach(SensitivePlistAttachment::new(self.clone())));
|
||||||
|
}
|
||||||
|
let dict = dict.unwrap();
|
||||||
|
let struct_data: T = plist::from_value(dict).map_err(|e| {
|
||||||
|
report!(
|
||||||
|
"Failed to deserialize plist struct for key '{}': {:?}",
|
||||||
|
key,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
.attach(SensitivePlistAttachment::new(
|
||||||
|
dict.as_dictionary().cloned().unwrap_or_default(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
Ok(struct_data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user