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