Skip to content

Commit

Permalink
questions endpoints (#33)
Browse files Browse the repository at this point in the history
questions endpoints
  • Loading branch information
subhojit777 committed Jun 17, 2019
2 parents a30db12 + a63edac commit db18dc3
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -15,3 +15,4 @@ futures = "^0.1"
env_logger = "^0.6"
failure = "^0.1.5"
reqwest = "^0.9.14"
actix = { version = "^0.7" }
52 changes: 52 additions & 0 deletions src/lib.rs
Expand Up @@ -98,12 +98,57 @@
//! "created": "2019-11-01T14:30:30"
//! }
//! ```
//!
//! #### ``/questions`
//!
//! **Method:** POST
//!
//! **Headers:**
//!
//! ```txt
//! Content type: application/json
//! Authorization: token <access_token>
//! ```
//!
//! **Body:**
//!
//! ```json
//! {
//! "title": "New Question",
//! "presentation_id": 1,
//! }
//! ```
//!
//! **Response:** 200 OK
//!
//! #### `/questions/{id}`
//!
//! **Method:** GET
//!
//! **Headers:**
//!
//! ```txt
//! Authorization: token <access_token>
//! ```
//!
//! **Response:**
//!
//! ```json
//! {
//! "id": 23,
//! "title": "New Question",
//! "created": "2019-11-01T14:30:30",
//! "presentation_id": 3,
//! "user_id": 7,
//! }
//! ```
extern crate chrono;
extern crate env_logger;
extern crate reqwest;
extern crate serde_json;
#[macro_use]
extern crate diesel;
extern crate actix;
extern crate actix_web;
extern crate dotenv;
extern crate failure;
Expand Down Expand Up @@ -133,6 +178,7 @@ pub mod helpers;
pub mod middleware;
pub mod models;
pub mod presentations;
pub mod questions;
pub mod schema;
pub mod session;

Expand Down Expand Up @@ -184,4 +230,10 @@ pub fn create_app() -> App<AppState> {
.resource("presentations/{id}", |r| {
r.method(Method::GET).with_async(presentations::get)
})
.resource("/questions", |r| {
r.method(Method::POST).with_async(questions::post)
})
.resource("/questions/{id}", |r| {
r.method(Method::GET).with_async(questions::get)
})
}
37 changes: 36 additions & 1 deletion src/models.rs
Expand Up @@ -6,7 +6,7 @@ use schema::presentations;
use schema::questions;
use serde_derive::*;

#[derive(Queryable, Identifiable, Associations)]
#[derive(Queryable, Serialize, Deserialize, Identifiable, Associations)]
#[belongs_to(Presentation, foreign_key = "presentation_id")]
#[table_name = "questions"]
pub struct Questions {
Expand All @@ -17,6 +17,41 @@ pub struct Questions {
pub user_id: i32,
}

/// Creates a new question.
#[derive(Insertable, Debug)]
#[table_name = "questions"]
pub struct NewQuestion {
pub title: String,
pub created: NaiveDateTime,
pub presentation_id: i32,
pub user_id: i32,
}

impl NewQuestion {
pub fn new(title: String, created: NaiveDateTime, presentation_id: i32, user_id: i32) -> Self {
NewQuestion {
title,
created,
presentation_id,
user_id,
}
}
}

/// The structure of the body of JSON request for creating a new question.
///
/// This is used for making the API request, and `NewQuestion` is used by the application for
/// creating the question in database.
#[derive(Deserialize, Serialize, Debug)]
pub struct NewQuestionJson {
pub title: String,
pub presentation_id: i32,
}

/// This defines an actor for retrieving question from database by id.
#[derive(Queryable, Deserialize)]
pub struct GetQuestion(pub i32);

#[derive(Queryable, Serialize, Deserialize, Identifiable)]
#[table_name = "answers"]
pub struct Answer {
Expand Down
136 changes: 136 additions & 0 deletions src/questions.rs
@@ -0,0 +1,136 @@
use actix::{Handler, Message};
use actix_web::middleware::session::RequestSession;
use actix_web::{AsyncResponder, Path};
use actix_web::{Error, HttpRequest, HttpResponse, Json, State};
use chrono::Utc;
use diesel::prelude::*;
use diesel::result::Error as DieselError;
use futures::Future;
use futures::IntoFuture;
use middleware::GitHubUserId;
use models::{GetQuestion, NewQuestion, NewQuestionJson, Questions};
use GH_USER_SESSION_ID_KEY;
use {AppState, DbExecutor};

impl Message for NewQuestion {
type Result = Result<(), DieselError>;
}

impl Handler<NewQuestion> for DbExecutor {
type Result = Result<(), DieselError>;

fn handle(&mut self, msg: NewQuestion, _ctx: &mut Self::Context) -> Self::Result {
use schema::questions::dsl::questions;
let connection: &MysqlConnection = &self.0.get().unwrap();

diesel::insert_into(questions)
.values(&msg)
.execute(connection)
.expect("Error saving the question");

Ok(())
}
}

impl Message for GetQuestion {
type Result = Result<Questions, DieselError>;
}

impl Handler<GetQuestion> for DbExecutor {
type Result = Result<Questions, DieselError>;

fn handle(&mut self, msg: GetQuestion, _ctx: &mut Self::Context) -> Self::Result {
use schema::questions::dsl::{id, questions};
let connection: &MysqlConnection =
&self.0.get().expect("Unable to get database connection.");

let result: Questions = questions.filter(id.eq(&msg.0)).first(connection)?;

Ok(result)
}
}

/// `/questions` POST
///
/// Headers:
///
/// Content type: application/json
/// Authorization: token <access_token>
///
/// Body:
/// ```json
/// {
/// "title": "New Question",
/// "presentation_id": 1,
/// }
/// ```
///
/// Response: 200 OK
pub fn post(
data: Json<NewQuestionJson>,
state: State<AppState>,
req: HttpRequest<AppState>,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
let gh_user_id_session = req
.session()
.get::<GitHubUserId>(GH_USER_SESSION_ID_KEY)
.into_future();

let now = Utc::now();

gh_user_id_session
.from_err()
.and_then(move |gh_user_id| {
let input = data.into_inner();
let new_question = NewQuestion::new(
input.title,
now.naive_utc(),
input.presentation_id,
gh_user_id.unwrap().id,
);

state
.db
.send(new_question)
.from_err()
.and_then(|response| match response {
Ok(_) => Ok(HttpResponse::Ok().finish()),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
})
})
.responder()
}

/// `/questions/{id}` GET
///
/// Headers:
///
/// Authorization: token <access_token>
///
/// Response:
/// ```json
/// {
/// "id": 23,
/// "title": "New Question",
/// "created": "2019-11-01T14:30:30",
/// "presentation_id": 3,
/// "user_id": 7,
/// }
/// ```
pub fn get(
data: Path<GetQuestion>,
req: HttpRequest<AppState>,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
let state: &AppState = req.state();

state
.db
.send(data.into_inner())
.from_err()
.and_then(|response| match response {
Ok(result) => Ok(HttpResponse::Ok().json(result)),
Err(DieselError::NotFound) => Ok(HttpResponse::NotFound().into()),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
})
.responder()
}

0 comments on commit db18dc3

Please sign in to comment.