make apis work by using rwlock/arc

This commit is contained in:
David Westgate 2024-04-26 22:29:31 -07:00
parent 9f312ba630
commit 3638b14b4c
3 changed files with 33 additions and 25 deletions

View File

@ -10,44 +10,50 @@ DELETE /questions/:questionId (empty body; return HTTP status code)
POST /answers (www-url-encoded body; return HTTP status code) POST /answers (www-url-encoded body; return HTTP status code)
* *
*/ */
pub async fn read_question(State(store): State<Store>, Path(id): Path<u8>) -> Response { pub async fn read_question(
match store.fetch(id) { State(store): State<Arc<RwLock<Store>>>,
Path(id): Path<u8>,
) -> Response {
match store.read().await.fetch(id) {
Ok(question) => question.to_dto(id).into_response(), Ok(question) => question.to_dto(id).into_response(),
Err(e) => (StatusCode::NOT_FOUND, e).into_response(), Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
} }
} }
pub async fn read_questions(State(store): State<Store>) -> Response { pub async fn read_questions(State(store): State<Arc<RwLock<Store>>>) -> Response {
(StatusCode::OK, Json(store.fetch_all())).into_response() (StatusCode::OK, Json(store.read().await.fetch_all())).into_response()
} }
pub async fn create_question( pub async fn create_question(
State(store): State<Store>, State(store): State<Arc<RwLock<Store>>>,
Json(question_dto): Json<QuestionDTO>, Json(question_dto): Json<QuestionDTO>,
) -> Response { ) -> Response {
//Normally, the server should generate the id, user provided id's (and the whole request) should be rejected. //Normally, the server should generate the id, user provided id's (and the whole request) should be rejected.
//QuestionDTO id then would be an option, but that makes to/from entity conversion more tricky.. todo //QuestionDTO id then would be an option, but that makes to/from entity conversion more tricky.. todo
let (id, question) = question_dto.to_entity(); let (id, question) = question_dto.to_entity();
match store.add(id, question) { match store.write().await.add(id, question) {
Ok(question) => (StatusCode::CREATED, Json(&question.to_dto(id))).into_response(), Ok(question) => (StatusCode::CREATED, Json(&question.to_dto(id))).into_response(),
Err(e) => (StatusCode::CONFLICT, e).into_response(), Err(e) => (StatusCode::CONFLICT, e).into_response(),
} }
} }
pub async fn update_question( pub async fn update_question(
State(store): State<Store>, State(store): State<Arc<RwLock<Store>>>,
Json(question_dto): Json<QuestionDTO>, Json(question_dto): Json<QuestionDTO>,
) -> Response { ) -> Response {
let (id, question) = question_dto.to_entity(); let (id, question) = question_dto.to_entity();
match store.update(id, question) { match store.write().await.update(id, question) {
Ok(question) => question.to_dto(id).into_response(), Ok(question) => question.to_dto(id).into_response(),
Err(e) => (StatusCode::NOT_FOUND, e).into_response(), Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
} }
} }
pub async fn delete_question(State(store): State<Store>, Path(id): Path<u8>) -> Response { pub async fn delete_question(
match store.remove(id) { State(store): State<Arc<RwLock<Store>>>,
Path(id): Path<u8>,
) -> Response {
match store.write().await.remove(id) {
Ok(question) => question.to_dto(id).into_response(), Ok(question) => question.to_dto(id).into_response(),
Err(e) => (StatusCode::NOT_FOUND, e).into_response(), Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
} }
} }
pub async fn create_answer(State(_store): State<Store> /*TODO */) -> Response { pub async fn create_answer(State(_store): State<Arc<RwLock<Store>>> /*TODO */) -> Response {
todo!() todo!()
} }

View File

@ -12,8 +12,9 @@ use axum::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
//use std::sync::Arc; use std::sync::Arc;
//use tokio::sync::{self,RwLock}; use store::Store;
use tokio::sync::RwLock;
async fn handle() -> Response { async fn handle() -> Response {
(StatusCode::OK, "Visiting the root").into_response() (StatusCode::OK, "Visiting the root").into_response()
@ -25,21 +26,21 @@ async fn handler_404() -> Response {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let store = store::Store::new(); let store: Arc<RwLock<Store>> = Arc::new(RwLock::new(store::Store::new()));
let ip: SocketAddr = SocketAddr::new([127, 0, 0, 1].into(), 3000); let ip: SocketAddr = SocketAddr::new([127, 0, 0, 1].into(), 3000);
let listener: tokio::net::TcpListener = tokio::net::TcpListener::bind(ip).await.unwrap(); let listener: tokio::net::TcpListener = tokio::net::TcpListener::bind(ip).await.unwrap();
let apis: Router = Router::new() let apis = Router::new()
.route("/question/:id", get(api::read_question)) .route("/question/:id", get(api::read_question))
.route("/questions", get(api::read_questions)) .route("/questions", get(api::read_questions))
.route("/question", post(api::create_question)) .route("/question", post(api::create_question))
.route("/question/:id", put(api::update_question)) .route("/question/:id", put(api::update_question))
.route("/question/:id", delete(api::delete_question)) .route("/question/:id", delete(api::delete_question))
.route("/answers", post(api::create_answer)) .route("/answers", post(api::create_answer))
//.nest(path, router)
.with_state(store) .with_state(store)
.fallback(handler_404); .fallback(handler_404);
let app: Router = Router::new().route("/", get(handle)).merge(apis);
let app = Router::new().route("/", get(handle)).merge(apis);
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }

View File

@ -15,14 +15,15 @@ impl Store {
} }
fn init() -> HashMap<u8, Question> { fn init() -> HashMap<u8, Question> {
let file = include_str!("./questions.json"); let file = include_str!("./questions.json");
let a: Vec<QuestionDTO> = serde_json::from_str(file).expect("can't read questions.json"); let questions_vec: Vec<QuestionDTO> =
println!("init"); serde_json::from_str(file).expect("can't read questions.json");
a.into_iter() questions_vec
.into_iter()
.map(|question_dto: QuestionDTO| question_dto.to_entity()) .map(|question_dto: QuestionDTO| question_dto.to_entity())
.collect() .collect()
} }
pub fn add(mut self, id: u8, question: Question) -> Result<Question, String> { pub fn add(&mut self, id: u8, question: Question) -> Result<Question, String> {
if self.questions.contains_key(&id) { if self.questions.contains_key(&id) {
return Err(format!("Question with id {} already exists", id)); return Err(format!("Question with id {} already exists", id));
} }
@ -31,22 +32,22 @@ impl Store {
_ => Err("Server Error".to_string()), _ => Err("Server Error".to_string()),
} }
} }
pub fn remove(mut self, id: u8) -> Result<Question, String> { pub fn remove(&mut self, id: u8) -> Result<Question, String> {
match self.questions.remove(&id) { match self.questions.remove(&id) {
Some(question) => Ok(question), Some(question) => Ok(question),
None => Err(format!("Question with id {} does not exist", id)), None => Err(format!("Question with id {} does not exist", id)),
} }
} }
pub fn fetch(self, id: u8) -> Result<Question, String> { pub fn fetch(&self, id: u8) -> Result<Question, String> {
match self.questions.get(&id) { match self.questions.get(&id) {
Some(question) => Ok(question.clone()), Some(question) => Ok(question.clone()),
None => Err(format!("Question with id {} does not exists", id)), None => Err(format!("Question with id {} does not exists", id)),
} }
} }
pub fn fetch_all(self) -> Vec<Question> { pub fn fetch_all(&self) -> Vec<Question> {
self.questions.values().cloned().collect() self.questions.values().cloned().collect()
} }
pub fn update(mut self, id: u8, question: Question) -> Result<Question, String> { pub fn update(&mut self, id: u8, question: Question) -> Result<Question, String> {
if !self.questions.contains_key(&id) { if !self.questions.contains_key(&id) {
return Err(format!("Question with id {} does not exists", id)); return Err(format!("Question with id {} does not exists", id));
} }