use async_trait::async_trait;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use smbcloud_ascapi_core::jsonapi::{CreateBody, CreateData, Document, Resource};
use smbcloud_ascapi_core::Client;
use smbcloud_ascapi_core::Result;
pub const RESOURCE_TYPE: &str = "certificates";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CertificateType {
#[serde(rename = "DEVELOPMENT")]
Development,
#[serde(rename = "DISTRIBUTION")]
Distribution,
#[serde(rename = "IOS_DEVELOPMENT")]
IosDevelopment,
#[serde(rename = "IOS_DISTRIBUTION")]
IosDistribution,
#[serde(rename = "MAC_APP_DEVELOPMENT")]
MacAppDevelopment,
#[serde(rename = "MAC_APP_DISTRIBUTION")]
MacAppDistribution,
#[serde(rename = "MAC_INSTALLER_DISTRIBUTION")]
MacInstallerDistribution,
#[serde(rename = "DEVELOPER_ID_APPLICATION")]
DeveloperIdApplication,
#[serde(rename = "DEVELOPER_ID_KEXT")]
DeveloperIdKext,
}
impl CertificateType {
pub fn as_api_str(self) -> &'static str {
match self {
Self::Development => "DEVELOPMENT",
Self::Distribution => "DISTRIBUTION",
Self::IosDevelopment => "IOS_DEVELOPMENT",
Self::IosDistribution => "IOS_DISTRIBUTION",
Self::MacAppDevelopment => "MAC_APP_DEVELOPMENT",
Self::MacAppDistribution => "MAC_APP_DISTRIBUTION",
Self::MacInstallerDistribution => "MAC_INSTALLER_DISTRIBUTION",
Self::DeveloperIdApplication => "DEVELOPER_ID_APPLICATION",
Self::DeveloperIdKext => "DEVELOPER_ID_KEXT",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CertificateAttributes {
pub name: Option<String>,
pub certificate_type: Option<CertificateType>,
pub display_name: Option<String>,
pub serial_number: Option<String>,
pub platform: Option<String>,
pub expiration_date: Option<String>,
pub certificate_content: Option<String>,
}
pub type Certificate = Resource<CertificateAttributes>;
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CertificateCreateAttributes {
pub csr_content: String,
pub certificate_type: CertificateType,
}
#[async_trait]
pub trait CertificatesApi {
async fn list_certificates(
&self,
filter_type: Option<CertificateType>,
) -> Result<Vec<Certificate>>;
async fn create_certificate(
&self,
attributes: CertificateCreateAttributes,
) -> Result<Certificate>;
async fn revoke_certificate(&self, id: &str) -> Result<()>;
}
#[async_trait]
impl CertificatesApi for Client {
async fn list_certificates(
&self,
filter_type: Option<CertificateType>,
) -> Result<Vec<Certificate>> {
let mut query = Vec::new();
if let Some(certificate_type) = filter_type {
query.push(("filter[certificateType]", certificate_type.as_api_str()));
}
query.push(("limit", "200"));
self.list_all::<CertificateAttributes>("/v1/certificates", &query)
.await
}
async fn create_certificate(
&self,
attributes: CertificateCreateAttributes,
) -> Result<Certificate> {
let body = CreateBody {
data: CreateData {
resource_type: RESOURCE_TYPE,
attributes,
relationships: None::<()>,
},
};
let doc: Document<CertificateAttributes> = self
.request(Method::POST, "/v1/certificates", &[], Some(&body))
.await?;
Ok(doc.data)
}
async fn revoke_certificate(&self, id: &str) -> Result<()> {
self.request_no_content(
Method::DELETE,
&format!("/v1/certificates/{id}"),
&[],
None::<&()>,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn certificate_type_round_trips_through_the_api_wire_format() {
for certificate_type in [
CertificateType::Development,
CertificateType::Distribution,
CertificateType::IosDevelopment,
CertificateType::IosDistribution,
CertificateType::MacAppDevelopment,
CertificateType::MacAppDistribution,
CertificateType::MacInstallerDistribution,
CertificateType::DeveloperIdApplication,
CertificateType::DeveloperIdKext,
] {
let json = serde_json::to_string(&certificate_type).expect("serializes");
let unquoted = json.trim_matches('"');
assert_eq!(
unquoted,
certificate_type.as_api_str(),
"serde and as_api_str disagree for {certificate_type:?}"
);
let parsed: CertificateType = serde_json::from_str(&json).expect("deserializes");
assert_eq!(parsed, certificate_type);
}
}
}