Add access management functions

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/2/head
trivernis 3 years ago
commit 9fe17d4c90
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

2
.gitignore vendored

@ -0,0 +1,2 @@
/target
Cargo.lock

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
</component>
</project>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/hydrus-api.iml" filepath="$PROJECT_DIR$/.idea/hydrus-api.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -0,0 +1,16 @@
[package]
name = "hydrus-api"
version = "0.1.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = "^1.0"
serde_derive = "^1.0"
reqwest = {version = "0.11.4", features = ["json"]}
[dev-dependencies.tokio]
version = "1.8.0"
features = ["macros", "rt-multi-thread"]

@ -0,0 +1,76 @@
use crate::error::Result;
use crate::paths::access_management::{
ApiVersionResponse, GetServicesResponse, SessionKeyResponse, VerifyAccessKeyResponse,
};
use crate::paths::Path;
use serde::de::DeserializeOwned;
use serde::Serialize;
static ACCESS_KEY_HEADER: &str = "Hydrus-Client-API-Access-Key";
pub struct Client {
inner: reqwest::Client,
base_url: String,
access_key: String,
}
impl Client {
pub fn new<S: AsRef<str>>(url: S, access_key: S) -> Result<Self> {
Ok(Self {
inner: reqwest::Client::new(),
access_key: access_key.as_ref().to_string(),
base_url: url.as_ref().to_string(),
})
}
/// Starts a get request to the path associated with the return type
async fn get<T: DeserializeOwned + Path, Q: Serialize + ?Sized>(
&mut self,
query: &Q,
) -> Result<T> {
let response: T = self
.inner
.get(format!("{}/{}", self.base_url, T::get_path()))
.header(ACCESS_KEY_HEADER, &self.access_key)
.query(query)
.send()
.await?
.json()
.await?;
Ok(response)
}
/// Stats a post request to the path associated with the return type
async fn post<T: DeserializeOwned + Path, B: Serialize>(&mut self, body: B) -> Result<T> {
let response: T = self
.inner
.post(format!("{}/{}", self.base_url, T::get_path()))
.json(&body)
.header(ACCESS_KEY_HEADER, &self.access_key)
.send()
.await?
.json()
.await?;
Ok(response)
}
/// Returns the current API version. It's being incremented every time the API changes.
pub async fn api_version(&mut self) -> Result<ApiVersionResponse> {
self.get(&()).await
}
/// Creates a new session key
pub async fn session_key(&mut self) -> Result<SessionKeyResponse> {
self.get(&()).await
}
/// Verifies if the access key is valid and returns some information about its permissions
pub async fn verify_access_key(&mut self) -> Result<VerifyAccessKeyResponse> {
self.get(&()).await
}
/// Returns the list of tag and file services of the client
pub async fn get_services(&mut self) -> Result<GetServicesResponse> {
self.get(&()).await
}
}

@ -0,0 +1,31 @@
use std::error::Error as StdError;
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Reqwest(reqwest::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reqwest(e) => {e.fmt(f)}
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Reqwest(e) => e.source(),
}
}
}
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Self::Reqwest(e)
}
}

@ -0,0 +1,6 @@
#[macro_use]
extern crate serde_derive;
pub mod paths;
pub mod client;
mod error;

@ -0,0 +1,47 @@
use crate::paths::common::BasicServiceInfo;
use crate::paths::Path;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiVersionResponse {
pub version: u32,
pub hydrus_version: u32,
}
impl Path for ApiVersionResponse {
fn get_path() -> String {
String::from("api_version")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionKeyResponse {
pub session_key: String,
}
impl Path for SessionKeyResponse {
fn get_path() -> String {
String::from("session_key")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyAccessKeyResponse {
pub basic_permissions: Vec<u32>,
pub human_description: String,
}
impl Path for VerifyAccessKeyResponse {
fn get_path() -> String {
String::from("verify_access_key")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetServicesResponse(pub HashMap<String, Vec<BasicServiceInfo>>);
impl Path for GetServicesResponse {
fn get_path() -> String {
String::from("get_services")
}
}

@ -0,0 +1,5 @@
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BasicServiceInfo {
pub name: String,
pub service_key: String,
}

@ -0,0 +1,6 @@
pub mod access_management;
pub mod common;
pub trait Path {
fn get_path() -> String;
}

@ -0,0 +1,6 @@
use hydrus_api::client::Client;
use std::env;
pub fn get_client() -> Client {
Client::new(env::var("HYDRUS_URL").unwrap(), env::var("HYDRUS_ACCESS_KEY").unwrap()).unwrap()
}

@ -0,0 +1,31 @@
mod common;
#[tokio::test]
async fn it_returns_the_api_version() {
let mut client = common::get_client();
let api_version = client.api_version().await.unwrap();
assert!(api_version.hydrus_version > 0);
assert!(api_version.version > 0);
}
#[tokio::test]
async fn it_returns_the_session_key() {
let mut client = common::get_client();
let session_key = client.session_key().await.unwrap();
assert!(session_key.session_key.len() > 0);
}
#[tokio::test]
async fn it_verifies_the_access_key() {
let mut client = common::get_client();
let verification_response = client.verify_access_key().await.unwrap();
assert!(verification_response.basic_permissions.len() > 0); // needs to be configured in the client but we want at least some permissions for the test
assert!(verification_response.human_description.len() > 0);
}
#[tokio::test]
async fn it_returns_a_list_of_services() {
let mut client = common::get_client();
let services_response = client.get_services().await.unwrap();
assert!(services_response.0.keys().len() > 0);
}
Loading…
Cancel
Save