any_type 0.5.0

A library for the Anytype API
Documentation
use crate::api::RequestFailure;
use http::HeaderMap;
use reqwest;
use serde_json::{Value, json};

/// A builder struct that is used to obtain an api_key
#[derive(Debug)]
pub struct CreateApiKeyRequest<'a> {
    server: &'a str,
    challenge_id: &'a str,
    code: &'a str,
}
impl<'a> CreateApiKeyRequest<'a> {
    /// Create a new CreateApiKeyRequest
    pub fn new(server: &'a str) -> Self {
        CreateApiKeyRequest {
            server,
            challenge_id: "",
            code: "",
        }
    }
    // Add the challenge_id that was generated by a [`CreateChallengeRequest`]
    pub fn challenge_id(mut self, challenge_id: &'a str) -> Self {
        self.challenge_id = challenge_id;
        self
    }
    // Add the code that will have been generated by the Anytype Desktop
    pub fn code(mut self, code: &'a str) -> Self {
        self.code = code;
        self
    }
    // Execute the CreateApiRequest returning a Result containing either a valid api_key or a RequestFailure object
    pub async fn send(&self) -> Result<String, RequestFailure> {
        let endpoint = "/v1/auth/api_keys".to_string();
        let body = json!({
            "challenge_id": self.challenge_id,
            "code": self.code
        });
        match send_to_server(self.server, &endpoint, &body).await {
            Ok(response) => {
                if response.status() == http::StatusCode::CREATED {
                    return Ok(response_to_api_key(response).await);
                } else {
                    let possible_status: Vec<http::StatusCode> = Vec::from([
                        http::StatusCode::BAD_REQUEST,
                        http::StatusCode::INTERNAL_SERVER_ERROR,
                    ]);
                    return Err(RequestFailure::api_error(response, possible_status).await);
                }
            }
            Err(e) => Err(RequestFailure::reqwest_error(e)),
        }
    }
}

/// A builder struct that is used to obtain a challenge_id and prompt the Anytype Desktop to generate and display the 4-digit code associated with it
#[derive(Debug)]
pub struct CreateChallengeRequest<'a> {
    server: &'a str,
    app_name: &'a str,
}
impl<'a> CreateChallengeRequest<'a> {
    /// Create a new CreateChallengeRequest object
    pub fn new(server: &'a str) -> Self {
        CreateChallengeRequest {
            server,
            app_name: "",
        }
    }
    /// Add the name of the app that will be associated with the api_key
    pub fn app_name(mut self, app_name: &'a str) -> Self {
        self.app_name = app_name;
        self
    }
    /// Execute the CreateChallengeRequest returning a Result containing either a challenge_id or a RequestFailure object
    pub async fn send(&self) -> Result<String, RequestFailure> {
        let endpoint = "/v1/auth/challenges".to_string();
        let body = json!({
            "app_name": self.app_name,
        });
        match send_to_server(self.server, &endpoint, &body).await {
            Ok(response) => {
                if response.status() == http::StatusCode::CREATED {
                    return Ok(response_to_challenge_id(response).await);
                } else {
                    let possible_status: Vec<http::StatusCode> = Vec::from([
                        http::StatusCode::BAD_REQUEST,
                        http::StatusCode::INTERNAL_SERVER_ERROR,
                    ]);
                    return Err(RequestFailure::api_error(response, possible_status).await);
                }
            }
            Err(e) => Err(RequestFailure::reqwest_error(e)),
        }
    }
}

async fn send_to_server(
    server: &str,
    endpoint: &str,
    body: &Value,
) -> Result<reqwest::Response, reqwest::Error> {
    let mut map = HeaderMap::new();
    map.insert("ACCEPT", "application/json".parse().unwrap());
    let client = reqwest::Client::new();
    let url = format!("{}{}", server, endpoint);
    client
        .post(url)
        .headers(map)
        .body(body.to_string())
        .send()
        .await
}

async fn response_to_api_key(response: reqwest::Response) -> String {
    let json_input = response.text().await.unwrap();
    let json: Value = serde_json::from_str(json_input.as_ref()).unwrap();
    json["api_key"].clone().as_str().unwrap().to_string()
}

async fn response_to_challenge_id(response: reqwest::Response) -> String {
    let json_input = response.text().await.unwrap();
    let json: Value = serde_json::from_str(json_input.as_ref()).unwrap();
    json["challenge_id"].clone().as_str().unwrap().to_string()
}