diff --git a/.gitignore b/.gitignore index 3d0fc473..719ca755 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ target .env -.vscode \ No newline at end of file +.vscode diff --git a/Cargo.lock b/Cargo.lock index ca07d93e..dee2c9a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,6 +581,31 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "filplus-core" +version = "0.1.0" +dependencies = [ + "actix-cors", + "actix-web", + "async-trait", + "base64 0.13.1", + "chrono", + "dotenv", + "env_logger", + "http", + "hyper", + "hyper-rustls", + "itertools", + "jsonwebtoken", + "markdown", + "octocrab", + "reqwest", + "serde", + "serde_json", + "structopt", + "tokio", +] + [[package]] name = "flate2" version = "1.0.26" @@ -621,31 +646,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fp-core" -version = "0.1.0" -dependencies = [ - "actix-cors", - "actix-web", - "async-trait", - "base64 0.13.1", - "chrono", - "dotenv", - "env_logger", - "http", - "hyper", - "hyper-rustls", - "itertools", - "jsonwebtoken", - "markdown", - "octocrab", - "reqwest", - "serde", - "serde_json", - "structopt", - "tokio", -] - [[package]] name = "futures" version = "0.3.28" diff --git a/Cargo.toml b/Cargo.toml index 1a0381d1..48f6d0b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "fp-core" +name = "filplus-core" version = "0.1.0" edition = "2021" diff --git a/src/core/mod.rs b/src/core/mod.rs index 967bc9ae..a815bd07 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,15 +1,16 @@ pub mod application; +use std::{ + fmt::Display, + pin::Pin, + task::{Context, Poll}, +}; + use actix_web::{ body::{BodySize, MessageBody}, web::Bytes, }; use chrono::Utc; use octocrab::models::{pulls::PullRequest, repos::ContentItems}; -use std::{ - fmt::Display, - pin::Pin, - task::{Context, Poll}, -}; use self::application::{ allocations::{AllocationRequest, ApplicationAllocationTypes, ApplicationAllocationsSigner}, @@ -127,6 +128,8 @@ impl MessageBody for LDNApplicationError { } impl LDNApplication { + /// Get Active Applications + /// Returns a list of all active applications pub async fn get_all_active_applications() -> Result, LDNApplicationError> { let gh: GithubWrapper = GithubWrapper::new(); @@ -144,7 +147,6 @@ impl LDNApplication { return (id, owner_name); }) .collect::>(); - dbg!(&pull_requests); let app_futures: Vec<_> = pull_requests .into_iter() .map(|i| tokio::spawn(LDNApplication::app_file_without_load(i.0))) @@ -169,6 +171,7 @@ impl LDNApplication { Ok(apps) } + /// Load Application By ID pub async fn load(application_id: String) -> Result { let gh: GithubWrapper = GithubWrapper::new(); let app_path = LDNPullRequest::application_path(&application_id); @@ -201,6 +204,7 @@ impl LDNApplication { } } + /// Create New Application pub async fn new(info: CreateApplicationInfo) -> Result { let application_id = info.application_id; let gh: GithubWrapper = GithubWrapper::new(); @@ -317,7 +321,7 @@ impl LDNApplication { } } - /// Move application from Governance Review to Proposal + /// Move application from Proposal to Approved pub async fn complete_new_application_proposal( &self, info: CompleteNewApplicationProposalInfo, @@ -370,7 +374,7 @@ impl LDNApplication { } } - /// Move application from Governance Review to Proposal + /// Merge Application Pull Request pub async fn merge_new_application_pr(&self) -> Result { match self.app_state().await { Ok(s) => match s { @@ -488,6 +492,7 @@ impl LDNApplication { Ok((parse_ldn_app_body(&issue_body), issue_creator)) } + /// Return Application state async fn app_state(&self) -> Result { let f = self.app_file().await?; Ok(f.info.application_lifecycle.get_state()) @@ -530,7 +535,7 @@ impl LDNApplication { } } - async fn _app_file_from_main(&self) -> Result { + async fn app_file_from_main(&self) -> Result { let app_path = LDNPullRequest::application_path(&self.application_id); let app_branch_name = "main"; match self.github.get_file(&app_path, &app_branch_name).await { @@ -815,7 +820,6 @@ impl LDNPullRequest { } } -#[cfg(test)] mod tests { use octocrab::models::issues::Issue; use tokio::time::{sleep, Duration}; @@ -825,10 +829,10 @@ mod tests { #[tokio::test] async fn end_to_end() { // Test Creating an application - let gh = GithubWrapper::new(); + let gh: GithubWrapper = GithubWrapper::new(); // let branches = gh.list_branches().await.unwrap(); - let issue = gh.list_issue(14).await.unwrap(); + let issue = gh.list_issue(63).await.unwrap(); let test_issue: Issue = gh .create_issue("from test", &issue.body.unwrap()) .await diff --git a/src/external_services/blockchain.rs b/src/external_services/blockchain.rs index 7b33a570..ca5aca68 100644 --- a/src/external_services/blockchain.rs +++ b/src/external_services/blockchain.rs @@ -1,16 +1,19 @@ const BASE_URL: &str = "https://api.filplus.d.interplanetary.one/public/api"; +/// BlockchainData is a client for the Fil+ blockchain data API. pub struct BlockchainData { client: reqwest::Client, base_url: String, } +/// BlockchainDataError is an error type for BlockchainData. #[derive(Debug)] pub enum BlockchainDataError { ReqwestError(reqwest::Error), } impl BlockchainData { + /// Setup new BlockchainData client. pub fn new() -> Self { use reqwest::header; let mut headers = header::HeaderMap::new(); @@ -31,6 +34,7 @@ impl BlockchainData { } } + /// Get Verified Clients pub async fn get_verified_clients(&self) -> Result { let query = "getVerifiedClients"; let url = self.build_url(query); @@ -51,6 +55,7 @@ impl BlockchainData { return Ok(body); } + /// Get Allowance For Address pub async fn get_allowance_for_address( &self, address: &str, @@ -68,6 +73,7 @@ impl BlockchainData { return Ok(body); } + /// Build URL fn build_url(&self, path: &str) -> String { format!("{}/{}", self.base_url, path) } diff --git a/src/main.rs b/src/main.rs index cef5ba4d..594bf295 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ extern crate markdown; use actix_web::middleware::Logger; -use actix_web::{web, App, HttpServer}; +use actix_web::{App, HttpServer}; use env_logger; -use std::sync::Mutex; pub(crate) mod b64; pub(crate) mod core; @@ -11,10 +10,21 @@ pub(crate) mod external_services; pub(crate) mod parsers; pub(crate) mod router; +/// Http Server Setup +/// Exposes Application and Blockchain endpoints +/// Application endpoints: +/// - Create Application +/// - Trigger Application +/// - Propose Application +/// - Approve Application +/// - Merge Application +/// - Get Application +/// - Get All Applications +/// Blockchain endpoints: +/// - Address Allowance +/// - Verified Clients #[tokio::main] async fn main() -> std::io::Result<()> { - let gh = external_services::github::GithubWrapper::new(); - let state = web::Data::new(Mutex::new(gh)); env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug")); HttpServer::new(move || { @@ -25,7 +35,6 @@ async fn main() -> std::io::Result<()> { App::new() .wrap(Logger::default()) .wrap(cors) - .app_data(state.clone()) .service(router::health) .service(router::application::create_application) .service(router::application::trigger_application) diff --git a/src/router/application.rs b/src/router/application.rs index 5496bd1a..4f6e9206 100644 --- a/src/router/application.rs +++ b/src/router/application.rs @@ -4,7 +4,21 @@ use crate::core::{ }; use actix_web::{get, post, web, HttpResponse, Responder}; -/// Create a new application +/// Create a new application. +/// +/// # Returns +/// Returns the application id. +/// +/// # Example +/// ```plaintext +/// curl --header "Content-Type: application/json" +/// --request POST +/// --data '{"application_id": "0x1234"}' +/// http://localhost:8080/application +/// ``` +/// +/// # Response +/// Created new application for issue: 0x1234 #[post("/application")] pub async fn create_application(info: web::Json) -> impl Responder { // HttpResponse::Ok().body(format!( "Created new application for issue:")) @@ -19,7 +33,27 @@ pub async fn create_application(info: web::Json) -> impl } } -/// Trigger an application +/// Trigger an application. +/// +/// # Returns +/// Returns the ApplicationFile. +/// +/// # Example +/// ```plaintext +/// curl --header "Content-Type: application/json" +/// --request POST +/// --data '{"actor": "JohnDoe"}' +/// http://localhost:8080/application/0x1234/trigger +/// ``` +/// +/// # Response +/// ```json +/// { +/// "id": "0x1234", +/// "_type": "ldn-v3", +/// .. +/// } +/// ``` #[post("/application/{id}/trigger")] pub async fn trigger_application( id: web::Path, @@ -42,7 +76,34 @@ pub async fn trigger_application( } } -/// Propose an application +/// Propose an application. +/// +/// # Returns +/// Returns the ApplicationFile. +/// +/// # Example +/// ```plaintext +/// curl --header "Content-Type: application/json" +/// --request POST +/// --data '{ +/// "signer": { +/// "signing_address": "0x1234567890abcdef1234567890abcdef12345678", +/// "time_of_signature": "2023-08-07T14:30:00Z", +/// "message_cid": "QmXYZ1234567890abcdef1234567890abcdef12345678" +/// }, +/// "request_id": "exampleRequestId123" +/// }' +/// http://localhost:8080/application/0x1234/propose +/// ``` +/// +/// # Response +/// ```json +/// { +/// "id": "0x1234", +/// "_type": "ldn-v3", +/// .. +/// } +/// ``` #[post("/application/{id}/propose")] pub async fn propose_application( id: web::Path, @@ -64,8 +125,34 @@ pub async fn propose_application( } } } - -/// Approve an application +/// Approve an application. +/// +/// # Returns +/// Returns the ApplicationFile. +/// +/// # Example +/// ```plaintext +/// curl --header "Content-Type: application/json" +/// --request POST +/// --data '{ +/// "signer": { +/// "signing_address": "0x1234567890abcdef1234567890abcdef12345678", +/// "time_of_signature": "2023-08-07T14:30:00Z", +/// "message_cid": "QmXYZ1234567890abcdef1234567890abcdef12345678" +/// }, +/// "request_id": "exampleRequestId123" +/// }' +/// http://localhost:8080/application/0x1234/approve +/// ``` +/// +/// # Response +/// ```json +/// { +/// "id": "0x1234", +/// "_type": "ldn-v3", +/// .. +/// } +/// ``` #[post("/application/{id}/approve")] pub async fn approve_application( id: web::Path, @@ -86,7 +173,26 @@ pub async fn approve_application( } } -/// Approve an application +/// Merge a previously proposed application. +/// +/// # Returns +/// Returns the ApplicationFile. +/// +/// # Example +/// ```plaintext +/// curl --header "Content-Type: application/json" +/// --request POST +/// http://localhost:8080/application/0x1234/merge +/// ``` +/// +/// # Response +/// ```json +/// { +/// "id": "0x1234", +/// "_type": "ldn-v3", +/// .. +/// } +/// ``` #[post("/application/{id}/merge")] pub async fn merge_application(id: web::Path) -> impl Responder { let ldn_application = match LDNApplication::load(id.into_inner()).await { @@ -101,7 +207,21 @@ pub async fn merge_application(id: web::Path) -> impl Responder { } } -/// Return an application +/// Retrieve an application based on its ID. +/// +/// # Example +/// ```plaintext +/// curl -X GET http://localhost:8080/application/0x1234 +/// ``` +/// +/// # Response +/// ```json +/// { +/// "id": "0x1234", +/// "_type": "ldn-v3", +/// .. +/// } +/// ``` #[get("/application/{id}")] pub async fn get_application(id: web::Path) -> actix_web::Result { let app = match LDNApplication::app_file_without_load(id.into_inner()).await { @@ -113,7 +233,24 @@ pub async fn get_application(id: web::Path) -> actix_web::Result actix_web::Result { let apps = match LDNApplication::get_all_active_applications().await { @@ -124,8 +261,15 @@ pub async fn get_all_applications() -> actix_web::Result { }; Ok(HttpResponse::Ok().body(serde_json::to_string_pretty(&apps).unwrap())) } - -/// Return server health status +/// Check the health status. +/// +/// # Example +/// ```plaintext +/// curl -X GET http://localhost:8080/health +/// ``` +/// +/// # Response +/// `OK` #[get("/health")] pub async fn health() -> impl Responder { HttpResponse::Ok().body("OK") diff --git a/src/router/blockchain.rs b/src/router/blockchain.rs index 1b955886..c8c6675c 100644 --- a/src/router/blockchain.rs +++ b/src/router/blockchain.rs @@ -1,6 +1,24 @@ use crate::external_services::blockchain::BlockchainData; use actix_web::{get, web, HttpResponse, Responder}; +/// Address Allowance. +/// +/// # Returns +/// Returns the allowance for a given address. +/// +/// # Example +/// ```plaintext +/// curl http://localhost:8080/blockchain/address_allowance/0x1234 +/// ``` +/// +/// # Response +/// ``` +/// { +/// "address": "0x1234", +/// "allowance": 10000 +/// } +/// ``` + #[get("/blockchain/address_allowance/{address}")] pub async fn address_allowance(address: web::Path) -> impl Responder { let blockchain = BlockchainData::new(); @@ -15,6 +33,24 @@ pub async fn address_allowance(address: web::Path) -> impl Responder { } } +/// Verified Clients. +/// +/// # Returns +/// Returns the list of verified clients. +/// +/// # Example +/// ```plaintext +/// curl http://localhost:8080/blockchain/verified_clients +/// ``` +/// +/// # Response +/// ``` +/// [ +/// "0x1234", +/// "0x5678" +/// ] +/// ``` + #[get("/blockchain/verified_clients")] pub async fn verified_clients() -> impl Responder { let blockchain = BlockchainData::new();