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::*;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Answer {
content: String,
question_id: u8,
pub struct NewAnswer {
pub content: String,
pub question_id: u8,
}
impl Answer {
pub fn _new(_id: u8, content: String, question_id: u8) -> Self {
Answer {
impl NewAnswer {
pub fn _new(content: String, question_id: u8) -> Self {
NewAnswer {
content,
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)]
pub struct AnswerDTO {
pub struct Answer {
id: u8,
content: String,
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
impl AnswerDTO {
pub fn to_entity(&self) -> (u8, Answer) {
(
self.id,
impl Answer {
pub fn new(id: u8, content: String, question_id: u8) -> Self {
Answer {
content: self.content.clone(),
question_id: self.question_id,
},
)
id,
content,
question_id,
}
}
impl IntoResponse for &AnswerDTO {
}
impl IntoResponse for &Answer {
fn into_response(self) -> Response {
(StatusCode::OK, Json(&self)).into_response()
}

View File

@ -1,6 +1,6 @@
/// All API route handlers of the application
use self::{
answer::AnswerDTO,
answer::NewAnswer,
pg_store::Store,
question::{NewQuestion, Question, QuestionDTO},
};
@ -99,8 +99,8 @@ 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 Unprocessable Entity 422 is returned (implicitlly) for a malformed body
pub async fn update_question(
State(store): State<Arc<RwLock<Store>>>,
Json(question): Json<Question>,
State(_store): State<Arc<RwLock<Store>>>,
Json(_question): Json<Question>,
) -> Response {
// Step 0: Update the question entry
// Step 1: Fetch the question_tags for the given tags
@ -130,8 +130,8 @@ pub async fn delete_question(
State(store): State<Arc<RwLock<Store>>>,
Path(id): Path<u8>,
) -> Response {
match store.write().await.remove_question(id) {
Ok(question) => question.into_response(),
match store.write().await.remove_question(id).await {
Ok(_) => (StatusCode::OK, format!("Question with id {} deleted", id)).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
pub async fn create_answer(
State(store): State<Arc<RwLock<Store>>>,
Form(answer_dto): Form<AnswerDTO>,
Form(new_answer): Form<NewAnswer>,
) -> Response {
let (id, answer) = answer_dto.to_entity();
match store.write().await.add_answer(id, answer) {
Ok(answer) => (StatusCode::CREATED, Json(&answer.to_dto(id))).into_response(),
match store.write().await.add_answer(new_answer).await {
Ok(answer) => (StatusCode::CREATED, Json(answer)).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,
/// and file I/O operations to persist these questions
/// 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> {
Err("To Do".to_string())
pub async fn remove_question(&mut self, id: u8) -> Result<bool, 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> {
@ -169,28 +178,57 @@ impl Store {
/// Remove tags from tags table, which have no question tag association
/// Returns true if any tags were removed, false otherwise
pub fn remove_orphan_tags(&mut self){
todo!()
pub async fn _remove_orphan_tags(&mut self) -> Result<bool, String> {
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
/// 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!()
}
///
pub fn sync_question_tags(&mut self, tags: Vec<Tag>){
pub fn _sync_question_tags(&mut self, _tags: Vec<Tag>) {
todo!()
}
pub fn update_question(&mut self, _question: Question) -> Result<Question, String> {
todo!()
}
pub fn add_answer(&mut self, _id: u8, _answer: Answer) -> Result<Answer, String> {
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")
.bind(question.title).bind(question.content).bind(question.id.to_string())
.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 async fn add_answer(&mut self, new_answer: NewAnswer) -> Result<Answer, String> {
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()),
}
}
}