highlevel-api 0.1.0

Unofficial Rust SDK for the GoHighLevel (HighLevel) API
Documentation
use reqwest::{Client, Response};
use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::RwLock;
use crate::{
    config::HighLevelConfig,
    error::{Error, Result},
};

pub struct HttpClient {
    inner: Client,
    pub base_url: String,
    pub api_version: String,
    token: RwLock<Option<String>>,
}

impl HttpClient {
    pub fn new(config: &HighLevelConfig) -> Self {
        Self {
            inner: Client::new(),
            base_url: config.base_url.clone(),
            api_version: config.api_version.clone(),
            token: RwLock::new(None),
        }
    }

    pub async fn set_token(&self, token: String) {
        *self.token.write().await = Some(token);
    }

    pub async fn get_token(&self) -> Option<String> {
        self.token.read().await.clone()
    }

    fn url(&self, path: &str) -> String {
        format!("{}{}", self.base_url, path)
    }

    async fn handle_response<T: DeserializeOwned>(&self, resp: Response) -> Result<T> {
        let status = resp.status();
        if status.is_success() {
            Ok(resp.json::<T>().await?)
        } else {
            let code = status.as_u16();
            let body = resp.text().await.unwrap_or_default();
            let message = serde_json::from_str::<serde_json::Value>(&body)
                .ok()
                .and_then(|v| {
                    v.get("message")
                        .and_then(|m| m.as_str())
                        .map(|s| s.to_string())
                })
                .unwrap_or(body);
            Err(Error::Api { status: code, message })
        }
    }

    async fn handle_no_content(&self, resp: Response) -> Result<()> {
        let status = resp.status();
        if status.is_success() {
            Ok(())
        } else {
            let code = status.as_u16();
            let message = resp.text().await.unwrap_or_else(|_| status.to_string());
            Err(Error::Api { status: code, message })
        }
    }

    pub async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .get(self.url(path))
            .header("Version", &self.api_version)
            .header("Content-Type", "application/json");
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn get_with_query<T: DeserializeOwned, Q: Serialize + ?Sized>(
        &self,
        path: &str,
        query: &Q,
    ) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .get(self.url(path))
            .header("Version", &self.api_version)
            .header("Content-Type", "application/json")
            .query(query);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn post<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .post(self.url(path))
            .header("Version", &self.api_version)
            .json(body);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn put<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .put(self.url(path))
            .header("Version", &self.api_version)
            .json(body);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn patch<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .patch(self.url(path))
            .header("Version", &self.api_version)
            .json(body);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn delete<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .delete(self.url(path))
            .header("Version", &self.api_version);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn delete_with_body<T: DeserializeOwned, B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> Result<T> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .delete(self.url(path))
            .header("Version", &self.api_version)
            .json(body);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_response(req.send().await?).await
    }

    pub async fn delete_no_content(&self, path: &str) -> Result<()> {
        let token = self.token.read().await.clone();
        let mut req = self
            .inner
            .delete(self.url(path))
            .header("Version", &self.api_version);
        if let Some(t) = token {
            req = req.bearer_auth(t);
        }
        self.handle_no_content(req.send().await?).await
    }
}