diff --git a/cmd/api/command.go b/cmd/api/command.go index 786bc48..44d147b 100644 --- a/cmd/api/command.go +++ b/cmd/api/command.go @@ -73,6 +73,71 @@ func (app *application) createCommandHandler(w http.ResponseWriter, r *http.Requ } } +func (app *application) updateCommandHandler(w http.ResponseWriter, r *http.Request) { + name, err := app.readCommandNameParam(r) + if err != nil { + app.notFoundResponse(w, r) + return + } + + command, err := app.Models.Commands.Get(name) + if err != nil { + switch { + case errors.Is(err, data.ErrRecordNotFound): + app.notFoundResponse(w, r) + default: + app.serverErrorResponse(w, r, err) + } + return + } + + var input struct { + Name *string `json:"name"` + Text *string `json:"text"` + Category *string `json:"category"` + Level *int `json:"level"` + } + + err = app.readJSON(w, r, &input) + if err != nil { + app.badRequestResponse(w, r, err) + return + } + + // There is a name since we successfully queried the database for + // a command, so no need to check != nil. + command.Name = *input.Name + + if input.Text != nil { + command.Text = *input.Text + } + + if input.Category != nil { + command.Category = *input.Category + } + + if input.Level != nil { + command.Level = *input.Level + } + + err = app.Models.Commands.Update(command) + if err != nil { + switch { + case errors.Is(err, data.ErrEditConflict): + app.editConflictResponse(w, r) + default: + app.serverErrorResponse(w, r, err) + } + return + } + + err = app.writeJSON(w, http.StatusOK, envelope{"command": command}, nil) + if err != nil { + app.serverErrorResponse(w, r, err) + } + +} + func (app *application) deleteCommandHandler(w http.ResponseWriter, r *http.Request) { name, err := app.readCommandNameParam(r) if err != nil { diff --git a/cmd/api/errors.go b/cmd/api/errors.go index f4a7d85..d5be1ff 100644 --- a/cmd/api/errors.go +++ b/cmd/api/errors.go @@ -38,3 +38,8 @@ func (app *application) notFoundResponse(w http.ResponseWriter, r *http.Request) message := "the requested resource could not be found" app.errorResponse(w, r, http.StatusNotFound, message) } + +func (app *application) editConflictResponse(w http.ResponseWriter, r *http.Request) { + message := "unable to update the record due to an edit conflict, please try again" + app.errorResponse(w, r, http.StatusConflict, message) +} diff --git a/cmd/api/routes.go b/cmd/api/routes.go index 20e9ac2..5259ca7 100644 --- a/cmd/api/routes.go +++ b/cmd/api/routes.go @@ -11,6 +11,7 @@ func (app *application) routes() *httprouter.Router { router.HandlerFunc(http.MethodGet, "/v1/commands/:name", app.showCommandHandler) router.HandlerFunc(http.MethodPost, "/v1/commands", app.createCommandHandler) + router.HandlerFunc(http.MethodPatch, "/v1/commands/:name", app.updateCommandHandler) router.HandlerFunc(http.MethodDelete, "/v1/commands/:name", app.deleteCommandHandler) return router } diff --git a/internal/data/commands.go b/internal/data/commands.go index 977374f..6c5f51f 100644 --- a/internal/data/commands.go +++ b/internal/data/commands.go @@ -8,9 +8,9 @@ import ( type Command struct { ID int `json:"id"` Name string `json:"name"` - Text string `json:"text"` - Category string `json:"category"` - Level int `json:"level"` + Text string `json:"text,omitempty"` + Category string `json:"category,omitempty"` + Level int `json:"level,omitempty"` } type CommandModel struct { @@ -46,6 +46,33 @@ func (c CommandModel) Get(name string) (*Command, error) { return &command, nil } +func (c CommandModel) Update(command *Command) error { + query := ` + UPDATE commands + SET text = $2, category = $3, level = $4 + WHERE name = $1 + RETURNING id` + + args := []interface{}{ + command.Name, + command.Text, + command.Category, + command.Level, + } + + err := c.DB.QueryRow(query, args...).Scan(&command.ID) + if err != nil { + switch { + case errors.Is(err, sql.ErrNoRows): + return ErrEditConflict + default: + return err + } + } + + return nil +} + // SetCategory queries the database for an entry with the provided name, // if there is one it updates the categories level with the provided level. func (c CommandModel) SetCategory(name string, category string) error { diff --git a/internal/data/models.go b/internal/data/models.go index d51ba34..9ca8e3b 100644 --- a/internal/data/models.go +++ b/internal/data/models.go @@ -8,6 +8,7 @@ import ( var ( ErrRecordNotFound = errors.New("record not found") ErrChannelRecordAlreadyExists = errors.New("channel already in database") + ErrEditConflict = errors.New("edit conflict") ErrCommandRecordAlreadyExists = errors.New("command already exists") ErrUserAlreadyExists = errors.New("user already in database") ) @@ -31,6 +32,7 @@ type Models struct { Commands interface { Get(name string) (*Command, error) Insert(command *Command) error + Update(command *Command) error SetLevel(name string, level int) error SetCategory(name, category string) error Delete(name string) error