Skip to content

Commit 855d767

Browse files
feat(api): create a dto for post
1 parent dfa64c6 commit 855d767

File tree

7 files changed

+121
-114
lines changed

7 files changed

+121
-114
lines changed

api.http

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
@host = https://devbcn.shuttleapp.rs
2-
# @host = http://localhost:8000
1+
# @host = https://devbcn.shuttleapp.rs
2+
@host = http://localhost:8080
3+
@film_id = 6f05e5f2-133c-11ee-be9f-0ab7e0d8c876
34

45
### health
56
GET {{host}}/api/health HTTP/1.1
@@ -9,7 +10,6 @@ POST {{host}}/api/v1/films HTTP/1.1
910
Content-Type: application/json
1011

1112
{
12-
"id": "356e42a8-e659-406f-98bb-6124414675e8",
1313
"title": "Death in Venice",
1414
"director": "Luchino Visconti",
1515
"year": 1971,
@@ -21,7 +21,7 @@ PUT {{host}}/api/v1/films HTTP/1.1
2121
Content-Type: application/json
2222

2323
{
24-
"id": "356e42a8-e659-406f-98bb-6124414675e8",
24+
"id": "{{film_id}}",
2525
"title": "Death in Venice",
2626
"director": "Benjamin Britten",
2727
"year": 1981,
@@ -32,11 +32,11 @@ Content-Type: application/json
3232
GET {{host}}/api/v1/films HTTP/1.1
3333

3434
### get film
35-
GET {{host}}/api/v1/films/356e42a8-e659-406f-98bb-6124414675e8 HTTP/1.1
35+
GET {{host}}/api/v1/films/{{film_id}} HTTP/1.1
3636

3737
### get bad film
3838
GET {{host}}/api/v1/films/356e42a8-e659-406f-98 HTTP/1.1
3939

4040

4141
### delete film
42-
DELETE {{host}}/api/v1/films/356e42a8-e659-406f-98bb-6124414675e8 HTTP/1.1
42+
DELETE {{host}}/api/v1/films/{{film_id}} HTTP/1.1

api/lib/src/film_repository/film_repository.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_trait::async_trait;
2-
use shared::models::Film;
2+
use shared::models::{CreateFilm, Film};
33
use uuid::Uuid;
44

55
pub type FilmError = String;
@@ -10,7 +10,7 @@ pub type FilmResult<T> = Result<T, FilmError>;
1010
pub trait FilmRepository: Send + Sync + 'static {
1111
async fn get_films(&self) -> FilmResult<Vec<Film>>;
1212
async fn get_film(&self, id: &Uuid) -> FilmResult<Film>;
13-
async fn create_film(&self, id: &Film) -> FilmResult<Film>;
13+
async fn create_film(&self, id: &CreateFilm) -> FilmResult<Film>;
1414
async fn update_film(&self, id: &Film) -> FilmResult<Film>;
1515
async fn delete_film(&self, id: &Uuid) -> FilmResult<Uuid>;
1616
}

api/lib/src/film_repository/memory_film_repository.rs

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::RwLock};
22

33
use async_trait::async_trait;
44
use chrono::Utc;
5-
use shared::models::Film;
5+
use shared::models::{CreateFilm, Film};
66
use uuid::Uuid;
77

88
use super::{film_repository::FilmResult, FilmRepository};
@@ -63,20 +63,21 @@ impl FilmRepository for MemoryFilmRepository {
6363
result
6464
}
6565

66-
async fn create_film(&self, film: &Film) -> FilmResult<Film> {
66+
async fn create_film(&self, create_film: &CreateFilm) -> FilmResult<Film> {
6767
match self.films.write() {
6868
Ok(mut films) => {
69-
if films.get(&film.id).is_some() {
70-
let err = format!("Film with id {} already exists", film.id);
71-
tracing::error!(err);
72-
Err(err)
73-
} else {
74-
let mut new_film = film.to_owned();
75-
new_film.created_at = Some(Utc::now());
76-
films.insert(film.id, new_film.clone());
77-
tracing::trace!("Film with id {} correctly created", film.id);
78-
Ok(new_film)
79-
}
69+
let new_film = Film {
70+
id: uuid::Uuid::new_v4(),
71+
title: create_film.title.clone(),
72+
director: create_film.director.clone(),
73+
year: create_film.year,
74+
poster: create_film.poster.clone(),
75+
created_at: Some(Utc::now()),
76+
updated_at: None,
77+
};
78+
films.insert(new_film.id, new_film.clone());
79+
tracing::trace!("Film with id {} correctly created", new_film.id);
80+
Ok(new_film)
8081
}
8182
Err(e) => {
8283
let err = format!("An error happened while trying to update film: {}", e);
@@ -133,7 +134,7 @@ impl FilmRepository for MemoryFilmRepository {
133134
mod tests {
134135
use super::MemoryFilmRepository;
135136
use crate::film_repository::FilmRepository;
136-
use shared::models::Film;
137+
use shared::models::{CreateFilm, Film};
137138
use std::{collections::HashMap, sync::RwLock};
138139

139140
fn create_test_film(id: &'static str) -> Film {
@@ -148,6 +149,15 @@ mod tests {
148149
}
149150
}
150151

152+
fn create_test_create_film(id: &'static str) -> CreateFilm {
153+
CreateFilm {
154+
title: format!("title-{}", id),
155+
director: format!("director-{}", id),
156+
poster: format!("poster-{}", id),
157+
year: 2001,
158+
}
159+
}
160+
151161
#[actix_rt::test]
152162
async fn repo_must_be_empty_on_new() {
153163
let repo = MemoryFilmRepository::new();
@@ -217,36 +227,20 @@ mod tests {
217227
#[actix_rt::test]
218228
async fn create_film_works() {
219229
let store = RwLock::new(HashMap::new());
220-
let mut film = create_test_film("1");
221-
film.created_at = None;
230+
let create_film = create_test_create_film("1");
222231

223232
let repo = MemoryFilmRepository { films: store };
224-
let result = repo.create_film(&film).await;
233+
let result = repo.create_film(&create_film).await;
225234

226235
assert!(result.is_ok());
227236
let created_file = result.unwrap();
228-
assert_eq!(created_file.id, film.id);
229-
assert_eq!(created_file.title, film.title);
230-
assert_eq!(created_file.director, film.director);
231-
assert_eq!(created_file.poster, film.poster);
232-
assert_eq!(created_file.year, film.year);
237+
assert_eq!(created_file.title, create_film.title);
238+
assert_eq!(created_file.director, create_film.director);
239+
assert_eq!(created_file.poster, create_film.poster);
240+
assert_eq!(created_file.year, create_film.year);
233241
assert!(created_file.created_at.is_some());
234242
}
235243

236-
#[actix_rt::test]
237-
async fn create_film_fails_if_the_film_already_exists() {
238-
let store = RwLock::new(HashMap::new());
239-
let film = create_test_film("1");
240-
store.write().unwrap().insert(film.id, film.clone());
241-
242-
let repo = MemoryFilmRepository { films: store };
243-
let result = repo.create_film(&film).await;
244-
245-
assert!(result.is_err());
246-
let err = result.unwrap_err();
247-
assert!(err.ends_with("already exists"));
248-
}
249-
250244
#[actix_rt::test]
251245
async fn update_film_works() {
252246
let store = RwLock::new(HashMap::new());

api/lib/src/film_repository/postgres_film_repository.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_trait::async_trait;
2-
use shared::models::Film;
2+
use shared::models::{CreateFilm, Film};
33
use uuid::Uuid;
44

55
use super::film_repository::{FilmRepository, FilmResult};
@@ -42,19 +42,18 @@ impl FilmRepository for PostgresFilmRepository {
4242
.map_err(|e| e.to_string())
4343
}
4444

45-
async fn create_film(&self, film: &Film) -> FilmResult<Film> {
45+
async fn create_film(&self, create_film: &CreateFilm) -> FilmResult<Film> {
4646
sqlx::query_as::<_, Film>(
4747
r#"
48-
INSERT INTO films (id, title, director, year, poster)
49-
VALUES ($1, $2, $3, $4, $5)
48+
INSERT INTO films (title, director, year, poster)
49+
VALUES ($1, $2, $3, $4)
5050
RETURNING id, title, director, year, poster, created_at, updated_at
5151
"#,
5252
)
53-
.bind(film.id)
54-
.bind(&film.title)
55-
.bind(&film.director)
56-
.bind(film.year as i16)
57-
.bind(&film.poster)
53+
.bind(&create_film.title)
54+
.bind(&create_film.director)
55+
.bind(create_film.year as i16)
56+
.bind(&create_film.poster)
5857
.fetch_one(&self.pool)
5958
.await
6059
.map_err(|e| e.to_string())

api/lib/src/v1/films.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use actix_web::{
22
web::{self, ServiceConfig},
33
HttpResponse,
44
};
5-
use shared::models::Film;
5+
use shared::models::{CreateFilm, Film};
66
use uuid::Uuid;
77

88
use crate::film_repository::FilmRepository;
@@ -36,8 +36,11 @@ async fn get<R: FilmRepository>(film_id: web::Path<Uuid>, repo: web::Data<R>) ->
3636
}
3737
}
3838

39-
async fn post<R: FilmRepository>(film: web::Json<Film>, repo: web::Data<R>) -> HttpResponse {
40-
match repo.create_film(&film).await {
39+
async fn post<R: FilmRepository>(
40+
create_film: web::Json<CreateFilm>,
41+
repo: web::Data<R>,
42+
) -> HttpResponse {
43+
match repo.create_film(&create_film).await {
4144
Ok(film) => HttpResponse::Ok().json(film),
4245
Err(e) => {
4346
HttpResponse::InternalServerError().body(format!("Internal server error: {:?}", e))
@@ -127,20 +130,34 @@ mod tests {
127130
#[actix_rt::test]
128131
async fn create_works() {
129132
let film_id = uuid::Uuid::new_v4();
130-
let film_title = "Film test title";
131-
let new_film = create_test_film(film_id, film_title.to_string());
133+
let title = "Film test title";
134+
let create_film = CreateFilm {
135+
title: title.to_string(),
136+
director: "Director test name".to_string(),
137+
year: 2001,
138+
poster: "Poster test name".to_string(),
139+
};
132140

133141
let mut repo = MockFilmRepository::default();
134-
repo.expect_create_film()
135-
.returning(|film| Ok(film.to_owned()));
142+
repo.expect_create_film().returning(move |create_film| {
143+
Ok(Film {
144+
id: film_id,
145+
title: create_film.title.to_owned(),
146+
director: create_film.director.to_owned(),
147+
year: create_film.year,
148+
poster: create_film.poster.to_owned(),
149+
created_at: Some(Utc::now()),
150+
updated_at: None,
151+
})
152+
});
136153

137-
let result = post(web::Json(new_film), web::Data::new(repo)).await;
154+
let result = post(web::Json(create_film), web::Data::new(repo)).await;
138155

139156
let body = to_bytes(result.into_body()).await.unwrap();
140157
let film = serde_json::from_slice::<'_, Film>(&body).unwrap();
141158

142159
assert_eq!(film.id, film_id);
143-
assert_eq!(film.title, film_title);
160+
assert_eq!(film.title, title);
144161
}
145162

146163
#[actix_rt::test]

0 commit comments

Comments
 (0)