ch9.8
This commit is contained in:
@@ -198,13 +198,13 @@ func (app *application) listMoviesHandler(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
movies, err := app.models.Movies.GetAll(input.Title, input.Genres, input.Filters)
|
movies, metadata, err := app.models.Movies.GetAll(input.Title, input.Genres, input.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.serverErrorResponse(w, r, err)
|
app.serverErrorResponse(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = app.writeJSON(w, http.StatusOK, envelope{"movies": movies}, nil)
|
err = app.writeJSON(w, http.StatusOK, envelope{"movies": movies, "metadata": metadata}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.serverErrorResponse(w, r, err)
|
app.serverErrorResponse(w, r, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/greenlight/internal/validator"
|
"gitea.local.lab/Lbenedar/greenlight/internal/validator"
|
||||||
@@ -13,6 +14,28 @@ type Filters struct {
|
|||||||
SortSafelist []string
|
SortSafelist []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Metadata struct {
|
||||||
|
CurrentPage int `json:"current_page,omitempty"`
|
||||||
|
PageSize int `json:"page_size,omitempty"`
|
||||||
|
FirstPage int `json:"first_page,omitempty"`
|
||||||
|
LastPage int `json:"last_page,omitempty"`
|
||||||
|
TotalRecords int `json:"total_records,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateMetadata(totalRecords, page, pageSize int) Metadata {
|
||||||
|
if totalRecords == 0 {
|
||||||
|
return Metadata{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Metadata{
|
||||||
|
CurrentPage: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
FirstPage: 1,
|
||||||
|
LastPage: int(math.Ceil(float64(totalRecords) / float64(pageSize))),
|
||||||
|
TotalRecords: totalRecords,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateFilters(v *validator.Validator, f Filters) {
|
func ValidateFilters(v *validator.Validator, f Filters) {
|
||||||
v.Check(f.Page > 0, "page", "must be greater than zero")
|
v.Check(f.Page > 0, "page", "must be greater than zero")
|
||||||
v.Check(f.Page <= 10_000_000, "page", "must be a maximum of 10 million")
|
v.Check(f.Page <= 10_000_000, "page", "must be a maximum of 10 million")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type Models struct {
|
|||||||
Get(id int64) (*Movie, error)
|
Get(id int64) (*Movie, error)
|
||||||
Update(movie *Movie) error
|
Update(movie *Movie) error
|
||||||
Delete(id int64) error
|
Delete(id int64) error
|
||||||
GetAll(title string, genres []string, filter Filters) ([]*Movie, error)
|
GetAll(title string, genres []string, filter Filters) ([]*Movie, Metadata, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,9 +135,9 @@ func (m MovieModel) Delete(id int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MovieModel) GetAll(title string, genres []string, filter Filters) ([]*Movie, error) {
|
func (m MovieModel) GetAll(title string, genres []string, filter Filters) ([]*Movie, Metadata, error) {
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
SELECT id, created_at, title, year, runtime, genres, version
|
SELECT count(*) over(), id, created_at, title, year, runtime, genres, version
|
||||||
FROM movies
|
FROM movies
|
||||||
WHERE (to_tsvector('simple', title) @@ plainto_tsquery('simple', $1) OR $1 = '')
|
WHERE (to_tsvector('simple', title) @@ plainto_tsquery('simple', $1) OR $1 = '')
|
||||||
AND (genres @> $2 OR $2 = '{}')
|
AND (genres @> $2 OR $2 = '{}')
|
||||||
@@ -147,18 +147,22 @@ func (m MovieModel) GetAll(title string, genres []string, filter Filters) ([]*Mo
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, title, pq.Array(genres), filter.limit(), filter.offset())
|
args := []any{title, pq.Array(genres), filter.limit(), filter.offset()}
|
||||||
|
|
||||||
|
rows, err := m.DB.QueryContext(ctx, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, Metadata{}, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
totalRecords := 0
|
||||||
movies := []*Movie{}
|
movies := []*Movie{}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var movie Movie
|
var movie Movie
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
|
&totalRecords,
|
||||||
&movie.ID,
|
&movie.ID,
|
||||||
&movie.CreatedAt,
|
&movie.CreatedAt,
|
||||||
&movie.Title,
|
&movie.Title,
|
||||||
@@ -168,16 +172,18 @@ func (m MovieModel) GetAll(title string, genres []string, filter Filters) ([]*Mo
|
|||||||
&movie.Version,
|
&movie.Version,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, Metadata{}, err
|
||||||
}
|
}
|
||||||
movies = append(movies, &movie)
|
movies = append(movies, &movie)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, Metadata{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return movies, nil
|
metadata := calculateMetadata(totalRecords, filter.Page, filter.PageSize)
|
||||||
|
|
||||||
|
return movies, metadata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockMovieModel struct{}
|
type MockMovieModel struct{}
|
||||||
@@ -198,8 +204,8 @@ func (m MockMovieModel) Delete(id int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MockMovieModel) GetAll(title string, genres []string, filter Filters) ([]*Movie, error) {
|
func (m MockMovieModel) GetAll(title string, genres []string, filter Filters) ([]*Movie, Metadata, error) {
|
||||||
return nil, nil
|
return nil, Metadata{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateMovie(v *validator.Validator, movie *Movie) {
|
func ValidateMovie(v *validator.Validator, movie *Movie) {
|
||||||
|
|||||||
Reference in New Issue
Block a user