use reqwest::Client;
use thiserror::Error;
use crate::api::{
CreateDnsRecordRequest, CreateDnsRecordResponse, DnsRecord, DnsRecordId, GeneralResponse,
ListDnsRecordsResponse, UpdateDnsRecordRequest,
};
#[derive(Debug, Error)]
pub enum SimplyClientError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("JSON deserialization error: {0}")]
Json(#[from] serde_json::Error),
#[error("API error: {0} ({1})")]
Api(u32, String),
}
pub struct SimplyClient {
account: String,
api_key: String,
base_url: String,
client: Client,
}
impl SimplyClient {
pub fn new(account: impl Into<String>, api_key: impl Into<String>) -> Self {
Self {
account: account.into(),
api_key: api_key.into(),
base_url: "https://api.simply.com/2/".to_string(),
client: Client::new(),
}
}
}
impl SimplyClient {
pub async fn list_dns_records(
&self,
domain: &str,
) -> Result<Vec<DnsRecord>, SimplyClientError> {
let url = format!(
"{}/my/products/{}/dns/records",
self.base_url.trim_end_matches('/'),
domain
);
let res = self
.client
.get(&url)
.basic_auth(&self.account, Some(&self.api_key))
.send()
.await?;
let resp: ListDnsRecordsResponse = res.json().await?;
Ok(resp.records.into_iter().map(|r| r.into()).collect())
}
pub async fn create_dns_record(
&self,
domain: &str,
req: CreateDnsRecordRequest,
) -> Result<Vec<DnsRecordId>, SimplyClientError> {
let url = format!(
"{}/my/products/{}/dns/records",
self.base_url.trim_end_matches('/'),
domain
);
let res = self
.client
.post(&url)
.basic_auth(&self.account, Some(&self.api_key))
.json(&req)
.send()
.await?;
let resp: CreateDnsRecordResponse = res.json().await?;
Ok(resp.record.unwrap_or_default())
}
pub async fn update_dns_record(
&self,
domain: &str,
record_id: DnsRecordId,
req: UpdateDnsRecordRequest,
) -> Result<(), SimplyClientError> {
let url = format!(
"{}/my/products/{}/dns/records/{}",
self.base_url.trim_end_matches('/'),
domain,
record_id.id,
);
let res = self
.client
.put(&url)
.basic_auth(&self.account, Some(&self.api_key))
.json(&req)
.send()
.await?;
let status = res.status();
if !status.is_success() {
let resp: GeneralResponse = res.json().await?;
return Err(SimplyClientError::Api(
status.as_u16().into(),
resp.message.unwrap_or_default(),
));
}
Ok(())
}
pub async fn delete_dns_record(
&self,
domain: &str,
record_id: DnsRecordId,
) -> Result<(), SimplyClientError> {
let url = format!(
"{}/my/products/{}/dns/records/{}",
self.base_url.trim_end_matches('/'),
domain,
record_id.id,
);
let res = self
.client
.delete(&url)
.basic_auth(&self.account, Some(&self.api_key))
.send()
.await
.map_err(SimplyClientError::Http)?;
let status = res.status();
if !status.is_success() {
let resp: GeneralResponse = res.json().await?;
return Err(SimplyClientError::Api(
status.as_u16().into(),
resp.message.unwrap_or_default(),
));
}
Ok(())
}
}