From 03257ef1e38ea74cdc4e488b559f5953e8dbda23 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Sat, 27 Apr 2024 01:46:30 -0700 Subject: [PATCH] comment code --- README.md | 1 - src/api.rs | 45 ++++++++++++++++++++++++++++++++++++--------- src/main.rs | 1 + src/question.rs | 3 +++ src/store.rs | 22 ++++++++++++++++------ 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1946b04..c10425f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ These are a few things still to be added * API testing tooling (`swagger`) * Coded API tests with mock data * Specific defined Error types for common errors -* Additional comments (rustdoc) * Serve basic web page(s) to utilize all APIs #### Lesser priority * Optimize flush/file writing: Write out the JSON in a pretty structure and avoid re-writing the whole file in cases when it can be avoid (like adding 1 item) diff --git a/src/api.rs b/src/api.rs index f935125..733513d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,14 +1,13 @@ +/// All API route handler of the application use self::{question::QuestionDTO, store::Store}; use crate::*; -/** -GET /questions (empty body; return JSON) -POST /questions (JSON body; return HTTP status code) -PUT /questions/:questionId (JSON body, return HTTP status code) -DELETE /questions/:questionId (empty body; return HTTP status code) -POST /answers (www-url-encoded body; return HTTP status code) - * - */ +/// Fetches a single question using a provided id +/// # Parameters +/// `id`: Path parmater Id of the question to lookup +/// # Returns +/// If the question is found, it converts the question to its Data Transfer Object (DTO) format and returns it. +/// If the question is not found, it returns a 404 NOT_FOUND response with the error message. pub async fn read_question( State(store): State>>, Path(id): Path, @@ -19,10 +18,22 @@ pub async fn read_question( } } +/// Fetches all questions +/// # Parameters +/// None (TODO - Support pagination from query parameters) +/// # Returns +/// A list of questions, empty if none exist pub async fn read_questions(State(store): State>>) -> Response { (StatusCode::OK, Json(store.read().await.fetch_all())).into_response() } +/// Creates a new question +/// # Parameters +/// `QuestionDTO` A JSON representation of a Question including its id +/// # Returns +/// Http Created 201 and the newly created question are return upon success +/// Http Confilct 409 and a message are returned if a question with that `id`` already exists +/// Http Unprocessable Entity 422 is returned (implicitlly) for a malformed body pub async fn create_question( State(store): State>>, Json(question_dto): Json, @@ -36,6 +47,14 @@ pub async fn create_question( } } +/// Updates an existing question +/// At present, questions cannot be 'partially updated' - all fields must be included +/// # Parameters +/// `QuestionDTO` A JSON representation of a Question including its id +/// # Returns +/// Http Ok 200 and the updated question are return upon success +/// Http Not Found 404 and a message are returned if a question with that `id` does not already exist +/// Http Unprocessable Entity 422 is returned (implicitlly) for a malformed body pub async fn update_question( State(store): State>>, Json(question_dto): Json, @@ -47,6 +66,12 @@ pub async fn update_question( } } +/// Delete an existing question +/// # Parameters +/// `id`: Path parmater Id of the question to delete +/// # Returns +/// Http Ok 200 and the deleted question are return upon success +/// Http Not Found 404 and a message are returned if a question with that `id` does not exist pub async fn delete_question( State(store): State>>, Path(id): Path, @@ -56,7 +81,9 @@ pub async fn delete_question( Err(e) => (StatusCode::NOT_FOUND, e).into_response(), } } - +/// Create an Answer +/// # Parameters +/// # Returns pub async fn create_answer(State(_store): State>> /*TODO */) -> Response { todo!() } diff --git a/src/main.rs b/src/main.rs index 1d4745f..01df484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ async fn handle() -> Response { (StatusCode::OK, "Visiting the root").into_response() } +//generic handler for any not supported route/method combination async fn handler_404() -> Response { (StatusCode::NOT_FOUND, "404 Not Found").into_response() } diff --git a/src/question.rs b/src/question.rs index 8c387a6..8d83cdd 100644 --- a/src/question.rs +++ b/src/question.rs @@ -1,3 +1,4 @@ +/// Contains struct definitions regarding questions use crate::*; #[derive(Deserialize, Serialize, Clone, Debug)] @@ -8,6 +9,7 @@ pub struct QuestionDTO { pub tags: Option>, } +/// Question Data Transfer Object, a representation of the expected serialized JSON formated of questions regarding requests, responses, and our question json file impl QuestionDTO { pub fn to_entity(&self) -> (u8, Question) { ( @@ -26,6 +28,7 @@ impl IntoResponse for &QuestionDTO { } } +/// Question 'entity' used for in-memory interactions of questions by the store #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Question { title: String, diff --git a/src/store.rs b/src/store.rs index 1256b42..e9708c5 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,6 +1,11 @@ +/// Store is responsible for manageing the in-memory hashmap of questions by providing initialization read/write functions, +/// and file I/O operations to persist these questions +/// TODO - Results returning errors should use specified types, not strings use self::question::{Question, QuestionDTO}; use crate::*; +const DB_PATH: &str = "./questions.json"; + #[derive(Debug)] pub struct Store { file: File, @@ -8,20 +13,21 @@ pub struct Store { } impl Store { + // Upon initialization, we need to read a questions.json if it exists and populate our questions hashmap from it. + // Otherwise we create questions.json. + // JSON formatting and I/O errors possible here are semi-handled with a message, but ultimetly we will panic in those cases pub fn new() -> Self { - let file: File = File::create_new("./questions.json") + let file: File = File::create_new(DB_PATH) .or_else(|e| { if e.kind() == ErrorKind::AlreadyExists { - File::options() - .read(true) - .write(true) - .open("./questions.json") + File::options().read(true).write(true).open(DB_PATH) } else { Err(e) } }) .unwrap(); let json = std::io::read_to_string(&file).expect("could not get json from file"); + // perhaps there is a more efficient/clever way aside from reading the json to a vector and mapping the vector to a hashmap. let questions_vec: Vec = serde_json::from_str(&json).expect("can't read questions.json"); let questions: HashMap = questions_vec @@ -31,6 +37,10 @@ impl Store { Store { questions, file } } + // Take the content of the questions hashmap, convert it to a vector of question DTOs and overwrite the file with these contents + // Not the most efficient approach if we are just adding or deleting a single question, but it does the job at our current scale + // 'flush' is also probably a misnomer + // TODO - pretty print before writing fn flush(&mut self) { let questions: &HashMap = &self.questions; let questions_vec: Vec = questions @@ -58,7 +68,7 @@ impl Store { None => { self.flush(); Ok(question) - } //none since key cannot already exist + } //Looks backwards, but insert must return none since key cannot already exist _ => Err("Server Error".to_string()), } }