Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/console/commands/debug/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::configuration::get_configuration;
use actix_web::{rt, post, web, HttpResponse, Result, http::header::ContentType};

pub struct JsonCommand {
line: usize,
column: usize,
payload: String
}

impl JsonCommand {
pub fn new(line: usize, column: usize, payload: String) -> Self {
Self { line, column, payload }
}
}

impl crate::console::commands::CallableTrait for JsonCommand {
fn call(&self) -> Result<(), Box<dyn std::error::Error>> {
let payload: String = std::fs::read_to_string(&self.payload)?;
let index = line_column_to_index(payload.as_ref(), self.line, self.column);
let prefix = String::from_utf8(<std::string::String as AsRef<[u8]>>::as_ref(&payload)[..index].to_vec()).unwrap();

println!("{}", prefix);
Ok(())
}
}

fn line_column_to_index(u8slice: &[u8], line: usize, column: usize) -> usize {
let mut l = 1;
let mut c = 0;
let mut i = 0;
for ch in u8slice {
i += 1;
match ch {
b'\n' => {
l += 1;
c = 0;
}
_ => {
c += 1;
}
}
if line == l && c == column {
break;
}
}
return i;
}
3 changes: 3 additions & 0 deletions src/console/commands/debug/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod json;

pub use json::*;
1 change: 1 addition & 0 deletions src/console/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod appclient;
pub mod debug;
mod callable;
pub mod mq;

Expand Down
24 changes: 21 additions & 3 deletions src/console/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ enum Commands {
#[command(subcommand)]
command: AppClientCommands,
},
Debug {
#[command(subcommand)]
command: DebugCommands,
},
MQ {
#[command(subcommand)]
command: AppMqCommands,
Expand All @@ -26,15 +30,24 @@ enum AppClientCommands {
},
}

#[derive(Debug, Subcommand)]
enum DebugCommands {
Json {
#[arg(long)]
line: usize,
#[arg(long)]
column: usize,
#[arg(long)]
payload: String,
},
}

#[derive(Debug, Subcommand)]
enum AppMqCommands {
Listen {
},
}

//todo add documentation about how to add a new command
//todo the helper from console should have a nicer display

fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();

Expand All @@ -48,6 +61,11 @@ fn get_command(cli: Cli) -> Result<Box<dyn stacker::console::commands::CallableT
stacker::console::commands::appclient::NewCommand::new(user_id),
)),
},
Commands::Debug { command } => match command {
DebugCommands::Json { line, column, payload } => Ok(Box::new(
stacker::console::commands::debug::JsonCommand::new(line, column, payload),
)),
},
Commands::MQ { command} => match command {
AppMqCommands::Listen {} => Ok(Box::new(
stacker::console::commands::mq::ListenCommand::new(),
Expand Down
23 changes: 0 additions & 23 deletions src/forms/project/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,3 @@ impl ProjectForm {
Ok(is_active)
}
}

pub(crate) async fn body_into_form(body: Bytes) -> actix_web::Result<forms::project::ProjectForm, Error> {
let body_bytes = actix_web::body::to_bytes(body).await.unwrap();
let body_str = str::from_utf8(&body_bytes)
.map_err(|err| JsonResponse::<forms::project::ProjectForm>::build().internal_server_error(err.to_string()))?;
let deserializer = &mut serde_json::Deserializer::from_str(body_str);
serde_path_to_error::deserialize(deserializer)
.map_err(|err| {
let msg = format!("{}:{:?}", err.path().to_string(), err);
JsonResponse::<forms::project::ProjectForm>::build().bad_request(msg)
})
.and_then(|mut form: forms::project::ProjectForm| {
if !form.validate().is_ok() {
let errors = form.validate().unwrap_err().to_string();
let err_msg = format!("Invalid data received {:?}", &errors);
tracing::debug!(err_msg);

return Err(JsonResponse::<models::Project>::build().form_error(errors));
}

Ok(form)
})
}
15 changes: 15 additions & 0 deletions src/helpers/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use actix_web::error::{ErrorBadRequest, ErrorConflict, ErrorInternalServerError,
use actix_web::web::Json;
use actix_web::Error;
use serde_derive::Serialize;
use std::convert::From;

#[derive(Serialize)]
pub(crate) struct JsonResponse<T> {
Expand Down Expand Up @@ -109,3 +110,17 @@ where
JsonResponseBuilder::default()
}
}

impl JsonResponse<String> {
pub fn bad_request<I: Into<String>>(msg: I) -> Error {
JsonResponse::<String>::build().bad_request( msg.into())
}

pub fn internal_server_error<I: Into<String>>(msg: I) -> Error {
JsonResponse::<String>::build().internal_server_error( msg.into())
}

pub fn not_found<I: Into<String>>(msg: I) -> Error {
JsonResponse::<String>::build().not_found(msg.into())
}
}
31 changes: 14 additions & 17 deletions src/routes/project/add.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
use crate::db;
use crate::forms;
use crate::forms::project::ProjectForm;
use crate::helpers::JsonResponse;
use crate::models;
use actix_web::{
post, web,
web::{Bytes, Data},
web::{Data},
Responder, Result,
};
use serde_json::Value;
use sqlx::PgPool;
use std::sync::Arc;
use std::str::FromStr;
use std::str;
use serde_valid::Validate;

#[tracing::instrument(name = "Add project.")]
#[post("")]
pub async fn item(
body: Bytes,
web::Json(request_json): web::Json<serde_json::Value>,
user: web::ReqData<Arc<models::User>>,
pg_pool: Data<PgPool>,
) -> Result<impl Responder> {
// @todo ACL
let form = forms::project::form::body_into_form(body.clone()).await?;
let project_name = form.custom.custom_stack_code.clone();

let body_bytes = actix_web::body::to_bytes(body).await.unwrap();
let body_str = str::from_utf8(&body_bytes)
.map_err(|err| JsonResponse::<forms::project::ProjectForm>::build().internal_server_error(err.to_string()))?;
let request_json = Value::from_str(body_str).unwrap();
tracing::debug!("Request json: {:?}", request_json);
let form: ProjectForm= serde_json::from_value(request_json.clone())
.map_err(|err| JsonResponse::bad_request(err.to_string()))?;
if !form.validate().is_ok() {
let errors = form.validate().unwrap_err();
return Err(JsonResponse::bad_request(errors.to_string()));
}

let body: Value = serde_json::to_value::<forms::project::ProjectForm>(form)
.or(serde_json::to_value::<forms::project::ProjectForm>(forms::project::ProjectForm::default()))
let project_name = form.custom.custom_stack_code.clone();
let body: Value = serde_json::to_value::<ProjectForm>(form)
.or(serde_json::to_value::<ProjectForm>(ProjectForm::default()))
.unwrap();

let project = models::Project::new(
Expand All @@ -45,7 +43,6 @@ pub async fn item(
.await
.map(|project| JsonResponse::build().set_item(project).ok("Ok"))
.map_err(|_| {
JsonResponse::<models::Project>::build().internal_server_error("Internal Server Error")
JsonResponse::internal_server_error("Internal Server Error")
})
}

13 changes: 6 additions & 7 deletions src/routes/project/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,23 @@ pub async fn item(
path: web::Path<(i32,)>,
pg_pool: web::Data<PgPool>,
) -> Result<impl Responder> {
/// Get project apps of logged user only
let (id,) = path.into_inner();
let id = path.0;

db::project::fetch(pg_pool.get_ref(), id)
.await
.map_err(|err| JsonResponse::<models::Project>::build().internal_server_error(err))
.map_err(|err| JsonResponse::internal_server_error(err.to_string()))
.and_then(|project| match project {
Some(project) if project.user_id != user.id => {
Err(JsonResponse::<models::Project>::build().not_found("not found"))
Err(JsonResponse::not_found("not found"))
}
Some(project) => Ok(JsonResponse::build().set_item(Some(project)).ok("OK")),
None => Err(JsonResponse::<models::Project>::build().not_found("not found")),
None => Err(JsonResponse::not_found("not found")),
})
}

#[tracing::instrument(name = "Get user's project list.")]
#[get("/user/{id}")]
pub async fn list(
pub async fn admin_list(
user: web::ReqData<Arc<models::User>>,
path: web::Path<(String,)>,
pg_pool: web::Data<PgPool>,
Expand All @@ -42,6 +41,6 @@ pub async fn list(

db::project::fetch_by_user(pg_pool.get_ref(), &user_id)
.await
.map_err(|err| JsonResponse::<models::Project>::build().internal_server_error(err))
.map_err(|err| JsonResponse::internal_server_error(err))
.map(|projects| JsonResponse::build().set_list(projects).ok("OK"))
}
41 changes: 16 additions & 25 deletions src/routes/project/update.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
use std::str::FromStr;
use crate::forms;
use crate::forms::project::ProjectForm;
use crate::helpers::JsonResponse;
use crate::models;
use crate::db;
use actix_web::{web, web::Data, Responder, Result, put};
use actix_web::{web, Responder, Result, put};
use serde_json::Value;
use serde_valid::Validate;
use sqlx::PgPool;
use std::sync::Arc;
use actix_web::web::Bytes;
use tracing::Instrument;
use std::str;

#[tracing::instrument(name = "Update project.")]
#[put("/{id}")]
pub async fn item(
path: web::Path<(i32,)>,
body: Bytes,
web::Json(request_json): web::Json<serde_json::Value>,
user: web::ReqData<Arc<models::User>>,
pg_pool: Data<PgPool>,
pg_pool: web::Data<PgPool>,
) -> Result<impl Responder> {
let id = path.0;
let mut project = db::project::fetch(pg_pool.get_ref(), id)
.await
.map_err(|err| JsonResponse::<models::Project>::build().internal_server_error(err))
.map_err(JsonResponse::internal_server_error)
.and_then(|project| match project {
Some(project) if project.user_id != user.id => {
Err(JsonResponse::<models::Project>::build().bad_request("Project not found"))
Err(JsonResponse::bad_request("Project not found"))
}
Some(project) => Ok(project),
None => Err(JsonResponse::<models::Project>::build().not_found("Project not found")),
None => Err(JsonResponse::not_found("Project not found")),
})?;

let body_bytes = actix_web::body::to_bytes(body.clone()).await.unwrap();
let body_str = str::from_utf8(&body_bytes)
.map_err(|err| JsonResponse::<forms::project::ProjectForm>::build().internal_server_error(err.to_string()))?;
let request_json = Value::from_str(body_str)?;
tracing::debug!("Request json: {:?}", request_json);

// @todo ACL
let form = forms::project::form::body_into_form(body.clone()).await?;
tracing::debug!("form data: {:?}", form);

if let Err(errors) = form.validate() {
return Err(JsonResponse::<models::Project>::build().form_error(errors.to_string()));
let form: ProjectForm= serde_json::from_value(request_json.clone())
.map_err(|err| JsonResponse::bad_request(err.to_string()))?;
if !form.validate().is_ok() {
let errors = form.validate().unwrap_err();
return Err(JsonResponse::bad_request(errors.to_string()));
}

let project_name = form.custom.custom_stack_code.clone();

if !form.is_readable_docker_image().await.is_ok() {
return Err(JsonResponse::<models::Project>::build().bad_request("Can not access docker image"));
return Err(JsonResponse::bad_request("Can not access docker image"));
}

let body: Value = serde_json::to_value::<forms::project::ProjectForm>(form)
.or(serde_json::to_value::<forms::project::ProjectForm>(forms::project::ProjectForm::default()))
let body: Value = serde_json::to_value::<ProjectForm>(form)
.or(serde_json::to_value::<ProjectForm>(ProjectForm::default()))
.unwrap();


project.name = project_name;
project.body = body;
project.request_json = request_json;


db::project::update(pg_pool.get_ref(), project)
.await
.map(|project| {
Expand All @@ -71,6 +62,6 @@ pub async fn item(
})
.map_err(|err| {
tracing::error!("Failed to execute query: {:?}", err);
JsonResponse::<models::Project>::build().internal_server_error("")
JsonResponse::internal_server_error("")
})
}
5 changes: 0 additions & 5 deletions src/routes/rating/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ use actix_web::{get, web, Responder, Result};
use sqlx::PgPool;
use tracing::Instrument;

// workflow
// add, update, list, get(user_id), ACL,
// ACL - access to func for a user
// ACL - access to objects for a user

#[tracing::instrument(name = "Get rating.")]
#[get("/{id}")]
pub async fn get_handler(
Expand Down
8 changes: 0 additions & 8 deletions src/routes/test/casbin.rs

This file was deleted.

1 change: 0 additions & 1 deletion src/routes/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
pub mod deploy;
pub mod casbin;
Loading