diff options
-rw-r--r-- | src/repository/models.rs | 68 | ||||
-rw-r--r-- | src/repository/routes.rs | 24 |
2 files changed, 90 insertions, 2 deletions
diff --git a/src/repository/models.rs b/src/repository/models.rs index 607008e..1fd1649 100644 --- a/src/repository/models.rs +++ b/src/repository/models.rs @@ -1,5 +1,6 @@ use crate::db::get_client; use crate::errors::{AppError, AppErrorType}; + use chrono::NaiveDateTime; use deadpool_postgres::{Client, Pool}; use serde::{Deserialize, Serialize}; @@ -7,6 +8,8 @@ use tokio_pg_mapper::FromTokioPostgresRow; use tokio_pg_mapper_derive::PostgresMapper; use uuid::Uuid; +use std::net::SocketAddr; + #[derive(Serialize, Deserialize, PostgresMapper)] #[pg_mapper(table = "repository")] /// Repository model @@ -18,6 +21,12 @@ pub struct Repository { pub uploader_ip: String, } +/// Struct used to create a new repository +#[derive(Serialize, Deserialize)] +pub struct RepositoryData { + pub url: String, +} + impl Repository { /// Find all repositories inside the database. /// Make a select query and order the repositories by descrescent updated @@ -116,4 +125,63 @@ impl Repository { } } + /// Create a new repository. It uses RepositoryData as support struct + pub async fn create( + pool: Pool, + data: &RepositoryData, + uploader_ip: Option<SocketAddr>, + ) -> Result<Repository, AppError> { + let client = get_client(pool.clone()).await.unwrap(); + + // Search a repository that matches with that url, because if it's + // exists, the server do not create a clone + let repo_search = Repository::search(&client, data.url.clone()).await; + match repo_search { + Ok(_) => { + return Err(AppError { + message: Some("Repository already exists".to_string()), + cause: Some("".to_string()), + error_type: AppErrorType::AuthorizationError, + }); + } + Err(_) => {} + }; + + let statement = client + .prepare(" + INSERT INTO repository(id, url, uploader_ip) VALUES($1, $2, $3) RETURNING * + ").await?; + + // Create a new UUID v4 + let uuid = Uuid::new_v4(); + + // Match the uploader ip + let user_ip = match uploader_ip { + Some(ip) => ip.to_string(), + None => { + return Err(AppError { + message: Some("Failed to fetch uploader ip".to_string()), + cause: Some("".to_string()), + error_type: AppErrorType::AuthorizationError, + }) + } + }; + + let repo = client + .query(&statement, &[&uuid, &data.url, &user_ip]) + .await? + .iter() + .map(|row| Repository::from_row_ref(row).unwrap()) + .collect::<Vec<Repository>>() + .pop(); + + match repo { + Some(repo) => Ok(repo), + None => Err(AppError { + message: Some("Error creating a new repository".to_string()), + cause: Some("Unknown error".to_string()), + error_type: AppErrorType::DbError, + }), + } + } } diff --git a/src/repository/routes.rs b/src/repository/routes.rs index 54bb35d..a0f4db5 100644 --- a/src/repository/routes.rs +++ b/src/repository/routes.rs @@ -1,7 +1,7 @@ use crate::config::AppState; use crate::errors::{AppError, AppErrorResponse, AppErrorType}; use crate::helpers::uuid_from_string; -use crate::repository::models::Repository; +use crate::repository::models::{Repository, RepositoryData}; use actix_web::http::header; use actix_web::{web, HttpRequest, HttpResponse, Responder}; use slog::info; @@ -80,11 +80,31 @@ async fn delete_repo( .map_err(|e| e) } +async fn create_repo( + req: HttpRequest, + payload: web::Json<RepositoryData>, + state: web::Data<AppState>, +) -> impl Responder { + info!(state.log, "POST /repo/"); + let request_from_ip = HttpRequest::peer_addr(&req); + let result = + Repository::create(state.pool.clone(), &payload, request_from_ip) + .await; + + result + .map(|repo| HttpResponse::Ok().json(repo)) + .map_err(|e| e) +} + /// Routes for repository. TODO: create endpoint for UPDATE method pub fn config(cfg: &mut web::ServiceConfig) { cfg.service( web::scope("/repo") - .service(web::resource("{_:/?}").route(web::get().to(index))) + .service( + web::resource("{_:/?}") + .route(web::get().to(index)) + .route(web::post().to(create_repo)), + ) .service( web::resource("/{id}{_:/?}") .route(web::get().to(get_repo)) |