implement tag DB functions

This commit is contained in:
David Westgate 2024-05-30 15:46:22 -07:00
parent 6e428b4b97
commit 132906b548
3 changed files with 79 additions and 51 deletions

View File

@ -2,47 +2,38 @@
use crate::*; use crate::*;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Answer { pub struct NewAnswer {
content: String, pub content: String,
question_id: u8, pub question_id: u8,
} }
impl Answer { impl NewAnswer {
pub fn _new(_id: u8, content: String, question_id: u8) -> Self { pub fn _new(content: String, question_id: u8) -> Self {
Answer { NewAnswer {
content, content,
question_id, question_id,
} }
} }
pub fn to_dto(&self, id: u8) -> AnswerDTO {
AnswerDTO {
id,
content: self.content.clone(),
question_id: self.question_id,
}
}
} }
#[derive(Deserialize, Serialize, Clone, Debug)] #[derive(Deserialize, Serialize, Clone, Debug)]
pub struct AnswerDTO { pub struct Answer {
id: u8, id: u8,
content: String, content: String,
question_id: u8, question_id: u8,
} }
/// Answer Data Transfer Object, a representation of the expected serialized JSON formated of answer regarding requests, responses, and our answer json file /// Answer Data Transfer Object, a representation of the expected serialized JSON formated of answer regarding requests, responses, and our answer json file
impl AnswerDTO { impl Answer {
pub fn to_entity(&self) -> (u8, Answer) { pub fn new(id: u8, content: String, question_id: u8) -> Self {
( Answer {
self.id, id,
Answer { content,
content: self.content.clone(), question_id,
question_id: self.question_id, }
},
)
} }
} }
impl IntoResponse for &AnswerDTO { impl IntoResponse for &Answer {
fn into_response(self) -> Response { fn into_response(self) -> Response {
(StatusCode::OK, Json(&self)).into_response() (StatusCode::OK, Json(&self)).into_response()
} }

View File

@ -1,6 +1,6 @@
/// All API route handlers of the application /// All API route handlers of the application
use self::{ use self::{
answer::AnswerDTO, answer::NewAnswer,
pg_store::Store, pg_store::Store,
question::{NewQuestion, Question, QuestionDTO}, question::{NewQuestion, Question, QuestionDTO},
}; };
@ -99,19 +99,19 @@ pub async fn create_question(
/// Http Not Found 404 and a message are returned if a question with that `id` does not already exist /// 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 /// Http Unprocessable Entity 422 is returned (implicitlly) for a malformed body
pub async fn update_question( pub async fn update_question(
State(store): State<Arc<RwLock<Store>>>, State(_store): State<Arc<RwLock<Store>>>,
Json(question): Json<Question>, Json(_question): Json<Question>,
) -> Response { ) -> Response {
// Step 0: Update the question entry // Step 0: Update the question entry
// Step 1: Fetch the question_tags for the given tags // Step 1: Fetch the question_tags for the given tags
// Step 2: Fetch the tags for the given question tags // Step 2: Fetch the tags for the given question tags
// Step 3: new_tags_labels = list of tag labels not already on question // Step 3: new_tags_labels = list of tag labels not already on question
// Step 3a: Fetch existing_new_tag as tags which already by given name exist, but do not yet have a question_tag association // Step 3a: Fetch existing_new_tag as tags which already by given name exist, but do not yet have a question_tag association
// Step 3b: Create question_tag association for 3a // Step 3b: Create question_tag association for 3a
// Step 3c: Create new tags which do not already exist // Step 3c: Create new tags which do not already exist
// Step 3d: Create question_tag association for 3c // Step 3d: Create question_tag association for 3c
// Step 4: remove_tag_labls = list of tag labs which should no longer have a question_tag association with the question // Step 4: remove_tag_labls = list of tag labs which should no longer have a question_tag association with the question
// Step 4a: Fetch existing_old_tags as tags by given na // Step 4a: Fetch existing_old_tags as tags by given na
// match store.write().await.update_question(question) { // match store.write().await.update_question(question) {
// Ok(question) => question.into_response(), // Ok(question) => question.into_response(),
@ -130,8 +130,8 @@ pub async fn delete_question(
State(store): State<Arc<RwLock<Store>>>, State(store): State<Arc<RwLock<Store>>>,
Path(id): Path<u8>, Path(id): Path<u8>,
) -> Response { ) -> Response {
match store.write().await.remove_question(id) { match store.write().await.remove_question(id).await {
Ok(question) => question.into_response(), Ok(_) => (StatusCode::OK, format!("Question with id {} deleted", id)).into_response(),
Err(e) => (StatusCode::NOT_FOUND, e).into_response(), Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
} }
} }
@ -145,11 +145,10 @@ pub async fn delete_question(
/// Status Unprocessable Entity 422 is returned (implicitlly) for a malformed body /// Status Unprocessable Entity 422 is returned (implicitlly) for a malformed body
pub async fn create_answer( pub async fn create_answer(
State(store): State<Arc<RwLock<Store>>>, State(store): State<Arc<RwLock<Store>>>,
Form(answer_dto): Form<AnswerDTO>, Form(new_answer): Form<NewAnswer>,
) -> Response { ) -> Response {
let (id, answer) = answer_dto.to_entity(); match store.write().await.add_answer(new_answer).await {
match store.write().await.add_answer(id, answer) { Ok(answer) => (StatusCode::CREATED, Json(answer)).into_response(),
Ok(answer) => (StatusCode::CREATED, Json(&answer.to_dto(id))).into_response(),
Err(e) => (StatusCode::CONFLICT, e).into_response(), Err(e) => (StatusCode::CONFLICT, e).into_response(),
} }
} }

View File

@ -1,3 +1,5 @@
use answer::NewAnswer;
/// Store is responsible for manageing the in-memory hashmap of questions by providing initialization read/write functions, /// 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 /// and file I/O operations to persist these questions
/// TODO - Results returning errors should use specified types, not strings /// TODO - Results returning errors should use specified types, not strings
@ -125,8 +127,15 @@ impl Store {
} }
} }
pub fn remove_question(&mut self, _id: u8) -> Result<Question, String> { pub async fn remove_question(&mut self, id: u8) -> Result<bool, String> {
Err("To Do".to_string()) let result = sqlx::query("DELETE FROM questions WHERE id = $1")
.bind(id.to_string())
.execute(&self.connection)
.await;
match result {
Ok(_) => Ok(true),
Err(e) => Err(e.to_string()),
}
} }
pub async fn fetch_one_question_by_id(&self, id: u8) -> Option<Question> { pub async fn fetch_one_question_by_id(&self, id: u8) -> Option<Question> {
@ -169,28 +178,57 @@ impl Store {
/// Remove tags from tags table, which have no question tag association /// Remove tags from tags table, which have no question tag association
/// Returns true if any tags were removed, false otherwise /// Returns true if any tags were removed, false otherwise
pub fn remove_orphan_tags(&mut self){ pub async fn _remove_orphan_tags(&mut self) -> Result<bool, String> {
todo!() let result = sqlx::query(
"DELETE FROM tags where id NOT IN (SELECT DISTINCT tag_id from question_tag)",
)
.execute(&self.connection)
.await;
match result {
Ok(_) => Ok(true),
Err(e) => Err(e.to_string()),
}
} }
/// Creates passed in tag labels in the tags table, only if they do not already exists /// Creates passed in tag labels in the tags table, only if they do not already exists
/// Returns list of all tags from passed in labels, regardless of if they already existed /// Returns list of all tags from passed in labels, regardless of if they already existed
pub fn add_tags_if_not_exists(&mut self, tags_labels: Vec<String>){ pub fn _add_tags_if_not_exists(&mut self, _tags_labels: Vec<String>) {
todo!() todo!()
} }
/// ///
pub fn sync_question_tags(&mut self, tags: Vec<Tag>){ pub fn _sync_question_tags(&mut self, _tags: Vec<Tag>) {
todo!() todo!()
} }
pub async fn _update_question(&mut self, question: Question) -> Result<Question, String> {
let result = sqlx::query("UPDATE questions SET title = $1 AND SET content = $2 WHERE id = $3 RETURNING id, title, content")
pub fn update_question(&mut self, _question: Question) -> Result<Question, String> { .bind(question.title).bind(question.content).bind(question.id.to_string())
todo!() .fetch_one(&self.connection).await;
match result {
Ok(pg_row) => Ok(Question::new(
Store::id_to_u8(&pg_row, "id"),
pg_row.get("title"),
pg_row.get("content"),
)),
Err(e) => Err(e.to_string()),
}
} }
pub fn add_answer(&mut self, _id: u8, _answer: Answer) -> Result<Answer, String> { pub async fn add_answer(&mut self, new_answer: NewAnswer) -> Result<Answer, String> {
todo!() let result =
sqlx::query("INSERT INTO answers VALUES ($1,$2) RETURNING id, content, question_id")
.bind(new_answer.content)
.bind(new_answer.question_id.to_string())
.fetch_one(&self.connection)
.await;
match result {
Ok(pg_row) => Ok(Answer::new(
Store::id_to_u8(&pg_row, "id"),
pg_row.get("content"),
Store::id_to_u8(&pg_row, "question_id"),
)),
Err(e) => Err(e.to_string()),
}
} }
} }