generated from OBJNULL/Dockerized-Rust
Imported API
This commit is contained in:
parent
9d58cf7f37
commit
9ab9074912
3 changed files with 215 additions and 0 deletions
101
project/src/api.rs
Normal file
101
project/src/api.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Libraries
|
||||||
|
mod retreiver;
|
||||||
|
mod bouncer;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use tokio::task;
|
||||||
|
use warp::{self, Filter};
|
||||||
|
|
||||||
|
use bouncer::ApiUser;
|
||||||
|
use crate::base::{conf::Conf, database::Database};
|
||||||
|
|
||||||
|
// Structures
|
||||||
|
pub struct API {
|
||||||
|
endpoint: String,
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
database: Arc<Mutex<Database>>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
async fn receive_request(params: HashMap<String, String>, api: Arc<API>) -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
// Checking reqiurements
|
||||||
|
if !params.contains_key("api") || !params.contains_key("symbol") {
|
||||||
|
let usage: String = format!("Usage: http(s)://path.to.neurostock/v1?api=[APIKEY]&symbol=[SYMBOL]\n");
|
||||||
|
return Ok(warp::reply::with_status(usage, warp::http::StatusCode::BAD_REQUEST));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting params
|
||||||
|
let symbol: &String = params.get("symbol").unwrap();
|
||||||
|
let key: &String = params.get("api").unwrap();
|
||||||
|
|
||||||
|
// Getting the API User
|
||||||
|
let user: ApiUser = bouncer::get_api_user(Arc::clone(&api.database), key).unwrap();
|
||||||
|
|
||||||
|
// Verifying the API Key
|
||||||
|
if !user.success {
|
||||||
|
return Ok(warp::reply::with_status(
|
||||||
|
format!("API Key Not Accepted by Server ({}).\n", user.message),
|
||||||
|
warp::http::StatusCode::FORBIDDEN
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return Ok(warp::reply::with_status(
|
||||||
|
format!("{}\n", api.recieve(symbol).unwrap()),
|
||||||
|
warp::http::StatusCode::OK
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementations
|
||||||
|
impl API {
|
||||||
|
// Constructors
|
||||||
|
pub fn init(conf: &Conf, database: Arc<Mutex<Database>>) -> Result<API, Box<dyn Error>> {
|
||||||
|
// Getting API Endpoint
|
||||||
|
let endpoint: String = conf.get_string("api.endpoint")?;
|
||||||
|
let port: u16 = conf.get_i32("api.port")? as u16;
|
||||||
|
|
||||||
|
// Returning API
|
||||||
|
return Ok(API {
|
||||||
|
endpoint: endpoint,
|
||||||
|
port: port,
|
||||||
|
|
||||||
|
database: database,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
pub fn start(&self, self_reference: Arc<API>) -> Result<task::JoinHandle<()>, Box<dyn Error>> {
|
||||||
|
// Getting the server port
|
||||||
|
let address: ([u8; 4], u16) = ([0,0,0,0], self.port.clone());
|
||||||
|
|
||||||
|
// Creating a WARP server
|
||||||
|
let warp_server = warp::path(self.endpoint.clone())
|
||||||
|
.and(warp::query::<HashMap<String, String>>())
|
||||||
|
.and(warp::any().map(move || Arc::clone(&self_reference)))
|
||||||
|
.and_then(receive_request);
|
||||||
|
|
||||||
|
// Starting the server on a new thread
|
||||||
|
let warp_future: task::JoinHandle<()> = task::spawn(async move {
|
||||||
|
warp::serve(warp_server).run(address).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return Ok(warp_future);
|
||||||
|
}
|
||||||
|
pub fn recieve(&self, symbol: &str) -> Result<String, Box<dyn Error>> {
|
||||||
|
// Getting a mutable reference for database
|
||||||
|
let database: &mut Database = {
|
||||||
|
&mut self.database.lock().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Getting result data
|
||||||
|
let result: serde_json::Value = retreiver::data_symbol(database, symbol)?;
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return Ok(result.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
68
project/src/api/bouncer.rs
Normal file
68
project/src/api/bouncer.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Libraries
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::base::database::Database;
|
||||||
|
|
||||||
|
// Structures
|
||||||
|
pub struct ApiUser {
|
||||||
|
pub success: bool,
|
||||||
|
pub message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
pub fn get_api_user(database: Arc<Mutex<Database>>, key: &str) -> Result<ApiUser, Box<dyn Error>> {
|
||||||
|
// Unlock Database Mutex
|
||||||
|
let db: &mut Database = {
|
||||||
|
&mut *database.lock().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initilize API table
|
||||||
|
db.inst_table("API", "`key` TEXT, `requests` INT, `limit` INT")?;
|
||||||
|
|
||||||
|
// Getting the API User with the key the requester has provided
|
||||||
|
let rows: Vec<mysql::Row> = db.get(
|
||||||
|
"API",
|
||||||
|
"*",
|
||||||
|
&format!(" WHERE `key`='{}'", key)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Going through all rows
|
||||||
|
for row in rows {
|
||||||
|
// Getting row details
|
||||||
|
let row_key: String = row.get(0).unwrap();
|
||||||
|
let row_requests: u32 = row.get(1).unwrap();
|
||||||
|
let row_limit: u32 = row.get(2).unwrap();
|
||||||
|
|
||||||
|
// Double check the API key
|
||||||
|
if row_key != key {continue;}
|
||||||
|
|
||||||
|
// Checking if our requests is lower than our limit
|
||||||
|
if row_requests >= row_limit {
|
||||||
|
return Ok(ApiUser {
|
||||||
|
success: false,
|
||||||
|
message: "API Request Limit Reached".to_string()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asking database to increase requests
|
||||||
|
db.update(
|
||||||
|
"API",
|
||||||
|
"requests",
|
||||||
|
&format!("{}", row_requests + 1),
|
||||||
|
&format!("`key`='{}'", key)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return Ok(ApiUser {
|
||||||
|
success: true,
|
||||||
|
message: "N/A".to_string()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't find our user
|
||||||
|
return Ok(ApiUser {
|
||||||
|
success: false,
|
||||||
|
message: "Invalid API Key".to_string()
|
||||||
|
});
|
||||||
|
}
|
||||||
46
project/src/api/retreiver.rs
Normal file
46
project/src/api/retreiver.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Libraries
|
||||||
|
use std::error::Error;
|
||||||
|
use serde_json::{self, json, Value};
|
||||||
|
|
||||||
|
use crate::base::database::Database;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
pub fn data_symbol(database: &mut Database, symbol: &str) -> Result<Value, Box<dyn Error>> {
|
||||||
|
// Checking if the table exists
|
||||||
|
if !database.exists_table(symbol)? {
|
||||||
|
return Ok(json!({
|
||||||
|
"message": format!("Couldn't find data for {}", symbol)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query Database to see if we have that symbol collected.
|
||||||
|
let rows: Vec<mysql::Row> = database.get(symbol, "*", "")?;
|
||||||
|
|
||||||
|
// Creating JSON result
|
||||||
|
let mut result: Vec<Value> = Vec::new();
|
||||||
|
|
||||||
|
// Going through all rows
|
||||||
|
for row in rows {
|
||||||
|
// Getting column data
|
||||||
|
let date: String = row.get(0).unwrap();
|
||||||
|
let open: f32 = row.get(1).unwrap();
|
||||||
|
let high: f32 = row.get(2).unwrap();
|
||||||
|
let low: f32 = row.get(3).unwrap();
|
||||||
|
let close: f32 = row.get(4).unwrap();
|
||||||
|
|
||||||
|
// Compiling result
|
||||||
|
result.push(json!({
|
||||||
|
"date": date,
|
||||||
|
"open": open,
|
||||||
|
"high": high,
|
||||||
|
"low": low,
|
||||||
|
"close": close
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return Ok(json!({
|
||||||
|
"symbol": symbol,
|
||||||
|
"data": result
|
||||||
|
}));
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue