Reorganize types

main
Drew Bednar 1 week ago
parent ea0138e827
commit 04489650ad

@ -3,6 +3,8 @@ package main
import (
"fmt"
"net/http"
"git.runcible.io/learning/pulley/internal/data"
)
// The logError() method is a generic helper for logging an error message along
@ -20,8 +22,7 @@ func (app *application) logError(r *http.Request, err error) {
// type for the message parameter, rather than just a string type, as this gives us
// more flexibility over the values that we can include in the response.
func (app *application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message any) {
env := envelope{"error": message}
env := data.ErrorMessageResponse{ErrorMessage: message}
err := app.writeJSON(w, status, env, nil)
if err != nil {
app.logError(r, err)

@ -36,7 +36,7 @@ func (app *application) createMovieHandler(w http.ResponseWriter, r *http.Reques
}
// use intermediate struct for decoding, use Movie struct for validation
m := &data.Movie{
movie := &data.Movie{
Title: input.Title,
Year: input.Year,
Runtime: input.Runtime,
@ -45,24 +45,24 @@ func (app *application) createMovieHandler(w http.ResponseWriter, r *http.Reques
v := validator.New()
if data.ValidateMovie(v, m); !v.Valid() {
if data.ValidateMovie(v, movie); !v.Valid() {
app.failedValidationResponse(w, r, v.Errors)
}
// We can implement a timeout context if we would like here
ctx := r.Context()
err = app.models.Movies.Insert(ctx, m)
err = app.models.Movies.Insert(ctx, movie)
if err != nil {
app.serverErrorResponse(w, r, err)
}
// Include location headers in HTTP response for newly created resource
headers := make(http.Header)
headers.Set("Location", fmt.Sprintf("/v1/movies/%d", m.ID))
headers.Set("Location", fmt.Sprintf("/v1/movies/%d", movie.ID))
// Write a JSON 201 Response with movie data in the response body and Location header
err = app.writeJSON(w, http.StatusCreated, envelope{"movie": m}, headers)
err = app.writeJSON(w, http.StatusCreated, data.MovieResponse{Movie: movie}, headers)
if err != nil {
app.serverErrorResponse(w, r, err)
}
@ -112,7 +112,7 @@ func (app *application) getMovieHandler(w http.ResponseWriter, r *http.Request)
}
}
err = app.writeJSON(w, http.StatusOK, envelope{"movie": movie}, nil)
err = app.writeJSON(w, http.StatusOK, data.MovieResponse{Movie: movie}, nil)
if err != nil {
//http.Error(w, "The server encountered a problem and could not process your request", http.StatusInternalServerError)
app.serverErrorResponse(w, r, err)
@ -192,7 +192,7 @@ func (app *application) updateMovieHandler(w http.ResponseWriter, r *http.Reques
return
}
err = app.writeJSON(w, http.StatusOK, envelope{"movie": movie}, nil)
err = app.writeJSON(w, http.StatusOK, data.MovieResponse{Movie: movie}, nil)
if err != nil {
app.serverErrorResponse(w, r, err)
}
@ -235,7 +235,7 @@ func (app *application) deleteMovieHandler(w http.ResponseWriter, r *http.Reques
// are machines then status code is alright.
//w.WriteHeader(http.StatusAccepted)
err = app.writeJSON(w, http.StatusOK, envelope{"message": "movie successfully deleted"}, nil)
err = app.writeJSON(w, http.StatusOK, data.GenericMessageResponse{Message: "movie successfully deleted"}, nil)
if err != nil {
app.serverErrorResponse(w, r, err)
}
@ -274,7 +274,9 @@ func (app *application) listMoviesHandler(w http.ResponseWriter, r *http.Request
app.serverErrorResponse(w, r, err)
return
}
app.writeJSON(w, 200, envelope{"movies": movies, "metadata": metadata}, nil)
// app.writeJSON(w, 200, envelope{"movies": movies, "metadata": metadata}, nil)
app.writeJSON(w, 200, data.ListMoviesResponse{Metadata: metadata, Movies: movies}, nil)
}
func (app *application) healthCheckHandler(w http.ResponseWriter, r *http.Request) {
@ -282,13 +284,13 @@ func (app *application) healthCheckHandler(w http.ResponseWriter, r *http.Reques
// js := `{"status": "available", "environment": %q, "version": %q}`
// js = fmt.Sprintf(js, app.config.env, Version)
// w.Write([]byte(js))
env := envelope{
"status": "available",
"system_info": map[string]string{
"environment": app.config.Env,
"version": Version,
},
}
// env := envelope{
// "status": "available",
// "system_info": map[string]string{
// "environment": app.config.Env,
// "version": Version,
// },
// }
// js, err := json.Marshal(data)
// if err != nil {
@ -301,7 +303,7 @@ func (app *application) healthCheckHandler(w http.ResponseWriter, r *http.Reques
// js = append(js, '\n')
// w.Header().Set("Content-Type", "application/json")
// w.Write(js)
err := app.writeJSON(w, 200, env, nil)
err := app.writeJSON(w, 200, data.HealthCheckResponse{Status: "available", SystemInfo: data.SystemInfo{Environment: app.config.Env, Version: Version}}, nil)
if err != nil {
//app.logger.Error(err.Error())
//http.Error(w, "The server encountered a problem and could not process your request", http.StatusInternalServerError)

@ -45,13 +45,20 @@ func TestHttpListHandlers(t *testing.T) {
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
assert.NilError(t, err)
content := make(map[string][]data.Movie)
/// If you use an anonymous struct be sure to use exported fields
// the encoding/json package can only work with exported fields
// content := new(struct {
// Metadata data.Metadata
// Movies []data.Movie
// })
content := new(data.ListMoviesResponse)
err = json.Unmarshal(body, &content)
assert.NilError(t, err)
assert.Equal(t, len(content["movies"]), 1)
assert.MovieEqual(t, content["movies"][0], movies[0])
t.Logf("Values: %v", content)
assert.Equal(t, len(content.Movies), 1)
assert.MovieEqual(t, *content.Movies[0], movies[0])
assert.Equal(t, content.Metadata.CurrentPage, 1)
assert.Equal(t, content.Metadata.TotalRecords, 1)
})
}

@ -344,14 +344,15 @@ func TestListMovieHandler(t *testing.T) {
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
assert.NilError(t, err)
jsonContent := make(map[string][]data.Movie)
// jsonContent := make(map[string][]data.Movie)
jsonContent := new(data.ListMoviesResponse)
err = json.Unmarshal(body, &jsonContent)
assert.NilError(t, err)
assert.Equal(t, len(jsonContent["movies"]), 2)
for i := range jsonContent["movies"] {
assert.MovieEqual(t, jsonContent["movies"][i], movies[i])
assert.Equal(t, len(jsonContent.Movies), 2)
for i := range jsonContent.Movies {
assert.MovieEqual(t, *jsonContent.Movies[i], movies[i])
}
err = mockPool.ExpectationsWereMet()

@ -18,7 +18,9 @@ import (
"github.com/julienschmidt/httprouter"
)
type envelope map[string]any
// We moved from a generic map envelope to specific types in internal/dto.go
// type envelope map[string]any
type envelope any
// This was the original function signature without enveloping
// func (app *application) writeJSON(w http.ResponseWriter, status int, data any, headers http.Header) error {

@ -0,0 +1,28 @@
package data
type ListMoviesResponse struct {
Metadata Metadata `json:"metadata"`
Movies []*Movie `json:"movies"`
}
type MovieResponse struct {
Movie *Movie `json:"movie"`
}
type SystemInfo struct {
Environment string `json:"environment"`
Version string `json:"version"`
}
type HealthCheckResponse struct {
Status string `json:"status"`
SystemInfo SystemInfo `json:"system_info"`
}
type GenericMessageResponse struct {
Message string `json:"message"`
}
type ErrorMessageResponse struct {
ErrorMessage any `json:"error"`
}
Loading…
Cancel
Save