-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #33 from pilksoc/backend
Add client for cache server
- Loading branch information
Showing
8 changed files
with
196 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use crate::kube::Kube; | ||
use crate::recipe::Recipe; | ||
use thiserror::Error; | ||
use reqwest::Url; | ||
use uuid::Uuid; | ||
|
||
#[derive(Debug, Error)] | ||
/// Errors while parsing the environment variable for the endpoint URL. | ||
pub enum EnvVarParseError { | ||
#[error("Error while getting environment variable. Have you included an equals sign?")] | ||
/// Used when the environment variable hasn't been set properly (such as a missing equals sign). | ||
EnvironmentError(#[from] std::env::VarError), | ||
#[error("Failed to parse URL, is it valid?")] | ||
/// Used when the URL can't be parsed, usually meaning that it's not been written correctly. | ||
UrlParseError(#[from] url::ParseError) | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
/// Errors while communicating with the REST cache server. | ||
pub enum RestError { | ||
#[error("Error while trying to reach REST server.")] | ||
/// Failed to establish connection to the REST server, or got another error (e.g. 404). | ||
ReqwestError(#[from] reqwest::Error), | ||
#[error("Could not parse JSON.")] | ||
/// Couldn't parse the JSON, usually because something else went wrong when trying to establish a connection to the server. | ||
SerdeError(#[from] serde_json::Error), | ||
} | ||
|
||
/// A client which interacts with the cache server. | ||
#[derive(Debug)] | ||
pub struct CacheClient { | ||
/// The endpoint URL. | ||
url: Url, | ||
} | ||
impl CacheClient { | ||
/// Create a new client using an environment variable. | ||
/// | ||
/// # Errors | ||
/// Will error when the environment variable isn't defined correctly, or if the URL couldn't be parsed. | ||
pub fn new() -> Result<CacheClient, EnvVarParseError> { | ||
let url_string = std::env::var("ENDPOINT")?; | ||
Ok(CacheClient { | ||
url: Url::parse(&url_string)?, | ||
}) | ||
} | ||
/// Create a new client using a given endpoint URL. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will error if the URL couldn't be parsed. | ||
pub fn from_url(url: &str) -> Result<CacheClient, url::ParseError> { | ||
Ok(CacheClient { | ||
url: Url::parse(url)?, | ||
}) | ||
} | ||
/// Gets all the Kubes from the cache server. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If the REST server connection fails, or if the output is an error (like 404), then this will return an Err. | ||
pub async fn get_kubes(&self) -> Result<Vec<Kube>, RestError> { | ||
let res = reqwest::get(self.url.join("kubes").unwrap()).await?.text().await?; | ||
Ok(serde_json::from_str(&res)?) | ||
} | ||
/// Gets all the recipes from the cache server. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If the REST server connection fails, or if the output is an error (like 404), then this will return an Err. | ||
pub async fn get_recipes(&self) -> Result<Vec<Recipe>, RestError> { | ||
let res = reqwest::get(self.url.join("kubeRecipes").unwrap()).await?.text().await?; | ||
Ok(serde_json::from_str(&res)?) | ||
} | ||
/// Gets a Kube by its ID from the cache server. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If the REST server connection fails, or if the output is an error (like 404), then this will return an Err. | ||
pub async fn get_kube_by_id(&self, id: Uuid) -> Result<Kube, RestError> { | ||
let res = reqwest::get(self.url.join(format!("kubeById/{}", id).as_str()).unwrap()).await?.text().await?; | ||
Ok(serde_json::from_str(&res)?) | ||
} | ||
/// Gets a recipe by its ID from the cache server. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If the REST server connection fails, or if the output is an error (like 404), then this will return an Err. | ||
pub async fn get_recipe_by_id(&self, id1: Uuid, id2: Uuid) -> Result<Recipe, RestError> { | ||
let res = reqwest::get(self.url.join(format!("kubeRecipeByIds/{}/{}", id1, id2).as_str()).unwrap()).await?.text().await?; | ||
Ok(serde_json::from_str(&res)?) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[tokio::test] | ||
async fn try_get_kubes() { | ||
let client = CacheClient::from_url("https://hack.djpiper28.co.uk/cache/").unwrap(); | ||
let mut kubes = client.get_kubes().await.unwrap(); | ||
let control_kubes_string = String::from( | ||
"[{\"name\":\"hydrogen\",\"id\":\"cdede93f-d0d7-4b4a-9fde-2a909444c58d\"},{\"name\":\"oxygen\",\"id\":\"8ddcf7ad-61f6-47ff-a49c-4abcec21d6a1\"},{\"name\":\"nitrogen\",\"id\":\"59a64f5b-bcf4-4d2d-bb7f-bc4eceaf41e5\"},{\"name\":\"calcium\",\"id\":\"2b006956-063d-4ca2-b75d-f6e5d67455c9\"},{\"name\":\"iron\",\"id\":\"5e930e14-4597-49b3-95fa-3e6dcc40b1ae\"},{\"name\":\"aluminium\",\"id\":\"d076033a-c583-4d38-8094-249a7fe2972b\"},{\"name\":\"uranium\",\"id\":\"82ac0ed4-62e3-4c5e-af3e-024c38285227\"},{\"name\":\"sodium\",\"id\":\"1c7fda1b-af90-411d-8162-8fd04c4890d3\"},{\"name\":\"chlorine\",\"id\":\"061f6efd-0067-4d71-92b8-a0b58562b906\"},{\"name\":\"light\",\"id\":\"e38ba705-58c1-469d-8eff-7cc01b94fd46\"},{\"name\":\"time\",\"id\":\"6991989a-f347-48eb-8c67-ade4cdc010d0\"},{\"name\":\"silicon\",\"id\":\"72650f96-011b-404d-aba0-2c8aa2f17aeb\"},{\"name\":\"water\",\"id\":\"8cf89e77-bf8b-4cf4-9941-46b853df4480\"},{\"name\":\"tap water\",\"id\":\"5ca230bc-135e-4be8-8be3-7c7c1b3e5484\"},{\"name\":\"salt\",\"id\":\"540710d4-5d7d-42f6-b9b7-aad181affbcf\"},{\"name\":\"sea water\",\"id\":\"74102256-00b5-42aa-9665-1bc81f13c18b\"},{\"name\":\"air\",\"id\":\"88bb179c-0d91-4322-a4e5-d3862bf83a31\"},{\"name\":\"rust\",\"id\":\"ad92587b-1643-469e-8357-1d6ec5ab6380\"},{\"name\":\"feldspar\",\"id\":\"2adfa430-faa4-4ecd-95e1-c6cc8c85f3b5\"},{\"name\":\"sand\",\"id\":\"1db355de-1404-4e7c-bf8b-a6b3355b9dc4\"},{\"name\":\"dirt\",\"id\":\"649e8325-530b-4abb-8fe0-5b79b395e84f\"},{\"name\":\"beach\",\"id\":\"03f9f164-be03-4de3-b5b4-c7549c1ef9e4\"},{\"name\":\"earth\",\"id\":\"66744f80-ccec-4c8b-9025-9edbb75df0a6\"},{\"name\":\"life\",\"id\":\"abd2f9a5-34cd-4b3f-941f-8cf934f6b967\"},{\"name\":\"age\",\"id\":\"bda3c6c5-3b79-418d-8d24-cc533a509065\"},{\"name\":\"energy\",\"id\":\"b4eba917-2179-4cd4-a64e-316fe005f11e\"},{\"name\":\"rock\",\"id\":\"3c545935-1382-4c3d-9771-1fa8492f1b77\"},{\"name\":\"fire\",\"id\":\"1e2e14df-f36b-43a4-9a2a-5112f84abb52\"},{\"name\":\"glass\",\"id\":\"12ec3f8d-0986-42d7-acc2-e6af8ba1842a\"}]" | ||
); | ||
let mut control_kubes: Vec<Kube> = serde_json::from_str(&control_kubes_string).unwrap(); | ||
kubes.sort(); | ||
control_kubes.sort(); | ||
assert_eq!(control_kubes, kubes) | ||
} | ||
#[tokio::test] | ||
async fn try_kube_by_id() { | ||
let client = CacheClient::from_url("https://hack.djpiper28.co.uk/cache/").unwrap(); | ||
let kube = client.get_kube_by_id(Uuid::parse_str("5e930e14-4597-49b3-95fa-3e6dcc40b1ae").unwrap()).await.unwrap(); | ||
let expected_string = String::from("{\"name\":\"iron\",\"id\":\"5e930e14-4597-49b3-95fa-3e6dcc40b1ae\"}"); | ||
let expected_kube: Kube = serde_json::from_str(&expected_string).unwrap(); | ||
assert_eq!(expected_kube, kube); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,48 @@ | ||
use std::str::FromStr; | ||
|
||
use uuid::Uuid; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] | ||
pub struct KubeId { | ||
uuid: Uuid, | ||
} | ||
|
||
impl KubeId { | ||
pub fn new(name: &str) -> Self { | ||
let mut name = name.to_string(); | ||
name.push_str("kube"); | ||
KubeId { | ||
uuid: Uuid::new_v5(&Uuid::NAMESPACE_DNS, name.as_bytes()), | ||
} | ||
} | ||
|
||
pub fn as_u128(&self) -> u128 { | ||
self.uuid.as_u128() | ||
} | ||
|
||
pub fn uuid(&self) -> Uuid { | ||
self.uuid | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] | ||
pub struct Kube { | ||
pub id: KubeId, | ||
pub id: Uuid, | ||
pub name: String, | ||
} | ||
impl Kube { | ||
pub fn new(name: String) -> Kube { | ||
let mut name_uuid = name.clone(); | ||
name_uuid.push_str("kube"); | ||
Kube { | ||
id: KubeId::new(name.as_str()), | ||
id: Uuid::new_v5(&Uuid::NAMESPACE_DNS, name_uuid.as_bytes()), | ||
name, | ||
} | ||
} | ||
pub fn from_name_uuid(name: &str, uuid: &str) -> Result<Kube, <Uuid as FromStr>::Err> { | ||
Ok(Kube { | ||
id: Uuid::from_str(uuid)?, | ||
name: name.to_string() | ||
}) | ||
} | ||
} | ||
|
||
// we should have a placeholder ''loading'' cube we can send over if api is slow | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
#[test] | ||
fn parse_kubes() { | ||
let input_json = String::from( | ||
"[{\"name\":\"hydrogen\",\"id\":\"153227c6-c069-4748-b5aa-aafac8abef00\"},{\"name\":\"oxygen\",\"id\":\"3ffe4c9e-5d35-42c9-a70e-1d80c544bdbb\"},{\"name\":\"nitrogen\",\"id\":\"bb155bf1-7200-45e7-b126-f2882f7aecaa\"}]" | ||
); | ||
let mut expected: Vec<Kube> = vec![ | ||
Kube::from_name_uuid("hydrogen", "153227c6-c069-4748-b5aa-aafac8abef00").unwrap(), | ||
Kube::from_name_uuid("oxygen", "3ffe4c9e-5d35-42c9-a70e-1d80c544bdbb").unwrap(), | ||
Kube::from_name_uuid("nitrogen", "bb155bf1-7200-45e7-b126-f2882f7aecaa").unwrap(), | ||
]; | ||
let mut kubes: Vec<Kube> = serde_json::from_str(&input_json).unwrap(); | ||
expected.sort(); | ||
kubes.sort(); | ||
assert_eq!(expected, kubes); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use uuid::Uuid; | ||
use crate::kube::Kube; | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Recipe { | ||
id: Uuid, | ||
output_id: Uuid, | ||
output_kube: Kube, | ||
kube1_id: Uuid, | ||
kube2_id: Uuid, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters