summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.rs20
-rw-r--r--src/main.rs24
-rw-r--r--src/models.rs17
-rw-r--r--src/schema.rs8
-rw-r--r--src/val.rs37
-rw-r--r--src/validator.rs51
-rw-r--r--src/views.rs45
7 files changed, 202 insertions, 0 deletions
diff --git a/src/db.rs b/src/db.rs
new file mode 100644
index 0000000..142aee5
--- /dev/null
+++ b/src/db.rs
@@ -0,0 +1,20 @@
+use diesel::prelude::*;
+use diesel::sqlite::SqliteConnection;
+use dotenv::dotenv;
+use std::env;
+use crate::models::Post;
+
+pub fn establish_connection() -> SqliteConnection {
+ dotenv().ok();
+
+ let database_url = env::var("DATABASE_URL")
+ .expect("DATABASE_URL must be set");
+ SqliteConnection::establish(&database_url)
+ .expect(&format!("Error connecting to {}", database_url))
+}
+
+pub fn create_post<'a>(conn: &SqliteConnection, post: &Post) -> Result<usize, diesel::result::Error> {
+ use crate::schema::posts;
+ // TODO(andrew): add error checking
+ return diesel::insert_into(posts::table).values(post).execute(conn);
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..e0e65a3
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,24 @@
+#[macro_use]
+extern crate diesel;
+extern crate dotenv;
+
+mod views;
+mod models;
+mod schema;
+mod db;
+mod validator;
+
+use actix_web::{App, HttpServer};
+
+#[actix_web::main]
+async fn main() -> std::io::Result<()> {
+ HttpServer::new(|| {
+ App::new()
+ .service(views::hello)
+ .service(views::echo)
+ .service(views::get_posts)
+ })
+ .bind("127.0.0.1:8080")?
+ .run()
+ .await
+}
diff --git a/src/models.rs b/src/models.rs
new file mode 100644
index 0000000..d025cf0
--- /dev/null
+++ b/src/models.rs
@@ -0,0 +1,17 @@
+use serde::{Serialize, Deserialize};
+use diesel::Queryable;
+use crate::schema::posts;
+
+#[derive(Queryable, Insertable, Serialize, Deserialize)]
+#[table_name="posts"]
+pub struct Post {
+ pub id: i32,
+ pub title: String,
+ pub body: String,
+ pub published: bool
+}
+
+#[derive(Serialize)]
+pub struct PostResponse<'a> {
+ pub msg: &'a str
+}
diff --git a/src/schema.rs b/src/schema.rs
new file mode 100644
index 0000000..d4dc4f4
--- /dev/null
+++ b/src/schema.rs
@@ -0,0 +1,8 @@
+table! {
+ posts (id) {
+ id -> Integer,
+ title -> Text,
+ body -> Text,
+ published -> Bool,
+ }
+}
diff --git a/src/val.rs b/src/val.rs
new file mode 100644
index 0000000..6cc7dfe
--- /dev/null
+++ b/src/val.rs
@@ -0,0 +1,37 @@
+mod validator;
+
+use serde::{Serialize, Deserialize};
+use serde_json;
+
+use validator::Validator;
+
+#[derive(Deserialize, Serialize)]
+struct Profile {
+ tg_id: i32,
+ fullname: String,
+ username: String,
+ is_admin: bool,
+ is_hidden: bool
+}
+
+
+fn test_validation() -> Result<(), String> {
+ let profile = Profile {
+ tg_id: 123,
+ fullname: String::from("Andrew Guschin"),
+ username: String::from("guschin"),
+ is_admin: true,
+ is_hidden: false
+ };
+
+ let request_data = serde_json::to_string(&profile).unwrap();
+
+ let validator = match Validator::from_file("src/schema.json") {
+ Ok(val) => val,
+ Err(msg) => return Err(msg)
+ };
+ let result = validator.check(&request_data);
+ println!("Is valid: {}", result);
+
+ return Ok(());
+}
diff --git a/src/validator.rs b/src/validator.rs
new file mode 100644
index 0000000..1fa52bc
--- /dev/null
+++ b/src/validator.rs
@@ -0,0 +1,51 @@
+extern crate serde;
+extern crate serde_json;
+extern crate valico;
+
+use std::fs::File;
+use std::io::BufReader;
+use std::io::Read;
+
+use serde_json::from_str;
+use valico::json_schema;
+
+pub struct Validator {
+ schema_content: String
+}
+
+impl Validator {
+ pub fn from_file(schema_path: &'static str) -> Result<Validator, String> {
+ let file = match File::open(schema_path) {
+ Ok(file) => file,
+ Err(_) => return Err(String::from("Could not open file"))
+ };
+ let mut buf_reader = BufReader::new(file);
+ let mut content = String::new();
+ match buf_reader.read_to_string(&mut content) {
+ Ok(_) => (),
+ Err(_) => return Err(String::from("Could not read from file"))
+ };
+ return Validator::from_string(content);
+ }
+
+ pub fn from_string(schema: String) -> Result<Validator, String> {
+ let s = match from_str(&schema) {
+ Ok(schema) => schema,
+ Err(_) => return Err(String::from("Could not parse schema"))
+ };
+ let mut scope = json_schema::Scope::new();
+ match scope.compile_and_return(s, true) {
+ Ok(_) => (),
+ Err(_) => return Err(String::from("Could not compile schema"))
+ };
+ return Ok(Validator { schema_content: schema });
+ }
+
+ pub fn check(&self, content: &str) -> bool {
+ let obj = from_str(content).unwrap();
+ let mut scope = json_schema::Scope::new();
+ let schema = from_str(&self.schema_content).unwrap();
+ let schema = scope.compile_and_return(schema, true).ok().unwrap();
+ return schema.validate(&obj).is_valid();
+ }
+}
diff --git a/src/views.rs b/src/views.rs
new file mode 100644
index 0000000..3622719
--- /dev/null
+++ b/src/views.rs
@@ -0,0 +1,45 @@
+use actix_web::{get, post, HttpResponse, Responder};
+use diesel::prelude::*;
+use crate::models::{Post, PostResponse};
+use crate::validator::Validator;
+use crate::db;
+use serde_json;
+
+#[get("/")]
+pub async fn hello() -> impl Responder {
+ HttpResponse::Ok().body("Hello world!")
+}
+
+#[post("/api/posts/add")]
+pub async fn echo(req_body: String) -> impl Responder {
+ let validator = Validator::from_file("schemas/post.json").unwrap();
+ let result = validator.check(&req_body);
+
+ if !result {
+ return HttpResponse::BadRequest().json(PostResponse {
+ msg: "Message doesn't conform to schema"
+ });
+ }
+
+ let conn = db::establish_connection();
+ let new_post: Post = serde_json::from_str(&req_body).unwrap();
+ let result = db::create_post(&conn, &new_post);
+ return match result {
+ Ok(_) => HttpResponse::Ok().json(new_post),
+ Err(res) => HttpResponse::InternalServerError().json(PostResponse {
+ msg: &res.to_string()
+ })
+ };
+}
+
+#[get("/api/posts")]
+pub async fn get_posts() -> impl Responder {
+ use crate::schema::posts::dsl::*;
+ let connection = db::establish_connection();
+ let results = posts.filter(published.eq(true))
+ .limit(5)
+ .load::<Post>(&connection)
+ .expect("Error loading posts");
+ HttpResponse::Ok().json(&results)
+}
+