This endpoint is different than the GET
ones because there is a request body that we receive from the client that we need to decode and turn into a Movie.
We have built an ADT that represents all possible validation errors for a NewMovieRequest
.
If the name
is empty, return a MovieNameTooShort
and if the synopsis
is empty, return a MovieSynopsisTooShort
.
We have two different models here. A NewMovieRequest
represents a request that has been successfully decoded containing a name
and synopsis
to save into the database.
However, at the point of decoding, we do not know whether the name
and synopsis
obey our business rules.
We need to validate this model and if it is valid, we create a different type ValidatedMovie
that represents this. The two types contain the same information, but by making them two distinct types, we can enforce additional type safety and better readability.
Build a validate
function that takes a NewMovieRequest
and returns either an Invalid[NonEmptyList[MovieValidationError]]
or a Valid[ValidatedMovie]
.
Remember what you learned from ValidationExercises
.
Complete exercise
Run unit test: NewMovieValidatorSpec
We can see SaveMovieService
has a saveMovie
function taken in as a dependency. It is of type ValidatedMovie => IO[MovieId]
.
The save
function accepts a NewMovieRequest
and returns a IO[ValidatedNel[MovieValidationError, MovieId]]
. We want to validate the request, if it is valid, we save the movie and return the MovieId
, otherwise we return all the errors.
Complete exercise
Run unit test: SaveMovieServiceSpec
The Controller
is a little different this time. We have the entire request as an argument to the function. We want to decode the request into a NewMovieRequest
and then pass that into the saveNewMovie
function in the class constructor.
After that, we want to attempt
as usual and handle each possibility.
The Decoder
s and Encoder
s that are necessary for this Controller
to work have been completed ahead of time:
Decoder[NewMovieRequest]
Encoder[MovieValidationError]
Encoder[MovieId]
Complete exercise
Run unit test: SaveMovieControllerSpec
The Service
and Controller
have been wired up in AppRuntime
and passed into AppRoutes
.
There is a POST
route that calls the saveMovieHandler
.
Note that req@POST...
means that req
is an alias for the value on the right hand side.
Start the app using ./auto/start-local
and test it out!
curl -H "Accept: application/json" -X POST -d "{\"name\": \"\", \"synopsis\": \"\"}" http://localhost:9200/movies
curl -H "Accept: application/json" -X POST -d "{\"name\": \"Space Jam\", \"synopsis\": \"A movie about basketball\"}" http://localhost:9200/movies
curl http://localhost:9200/movies/2