Skip to content

Commit

Permalink
refactor(http): use problem details
Browse files Browse the repository at this point in the history
  • Loading branch information
mgjules committed Apr 11, 2022
1 parent ab0ef1a commit bc609ae
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ linters-settings:
disable: false
arguments:
- maxLitCount: "3"
allowStrs: '""'
allowStrs: '"","error"'
allowInts: "0,1,2"
allowFloats: "0.0,0.,1.0,1.,2.0,2."
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic
Expand Down
60 changes: 60 additions & 0 deletions http/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package http

import "github.com/JulesMike/spoty/json"

// Error represents a problem details object as described in RFC 7807.
type Error struct {
// A URI reference [RFC3986] that identifies the
// problem type. This specification encourages that, when
// dereferenced, it provide human-readable documentation for the
// problem type (e.g., using HTML [W3C.REC-html5-20141028]). When
// this member is not present, its value is assumed to be
// "about:blank".
Type string `json:"type"`
// A short, human-readable summary of the problem
// type. It SHOULD NOT change from occurrence to occurrence of the
// problem, except for purposes of localization (e.g., using
// proactive content negotiation; see [RFC7231], Section 3.4).
Title string `json:"title"`
// The HTTP status code ([RFC7231], Section 6)
// generated by the origin server for this occurrence of the problem.
Status int `json:"status,omitempty"`
// A human-readable explanation specific to this
// occurrence of the problem.
Detail string `json:"detail,omitempty"`
// A URI reference that identifies the specific
// occurrence of the problem. It may or may not yield further
// information if dereferenced.
Instance string `json:"instance,omitempty"`
// Extensions contains additional data.
Extensions map[string]any `json:"extensions,omitempty"`
}

// NewError returns a new Error.
func NewError(
errType,
title string,
status int,
detail string,
instance string,
exts map[string]any,
) *Error {
if errType == "" {
errType = "about:blank"
}

return &Error{
Type: errType,
Title: title,
Status: status,
Detail: detail,
Instance: instance,
Extensions: exts,
}
}

func (e *Error) Error() string {
m, _ := json.Marshal(e) //nolint:errcheck

return string(m)
}
64 changes: 45 additions & 19 deletions http/handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package http

import (
"errors"
"net/http"

"github.com/JulesMike/spoty/docs"
Expand All @@ -11,21 +10,11 @@ import (
"github.com/swaggo/gin-swagger/swaggerFiles"
)

var (
errRetrieveCurrentTrack = errors.New("failed to retrieve current playing track")
errProcessCurrentTrack = errors.New("failed to process images for currently playing track")
)

// Success defines the structure for a successful response.
type Success struct {
Success string `json:"success"`
}

// Error defines the structure for a failed response.
type Error struct {
Error string `json:"error"`
}

// handleHealthCheck godoc
// @Summary Health Check
// @Description checks if server is running
Expand Down Expand Up @@ -81,8 +70,17 @@ func (s *Server) handleCurrentTrack(c *gin.Context) {

track, err := s.spoty.TrackCurrentlyPlaying(ctx)
if err != nil {
s.logger.ErrorwContext(ctx, errRetrieveCurrentTrack.Error(), "error", err.Error())
c.AbortWithStatusJSON(http.StatusNotFound, Error{Error: errRetrieveCurrentTrack.Error()})
rErr := NewError(
"no-playing-track",
"No track playing currently.",
http.StatusNotFound,
err.Error(),
c.Request.URL.String(),
nil,
)

s.logger.ErrorwContext(ctx, "failed to retrieve current playing track", "error", rErr.Error())
c.AbortWithStatusJSON(http.StatusNotFound, rErr)

return
}
Expand All @@ -105,20 +103,37 @@ func (s *Server) handleCurrentTrackImages(c *gin.Context) {

track, err := s.spoty.TrackCurrentlyPlaying(ctx)
if err != nil {
s.logger.ErrorwContext(ctx, errRetrieveCurrentTrack.Error(), "error", err.Error())
c.AbortWithStatusJSON(http.StatusNotFound, Error{Error: errRetrieveCurrentTrack.Error()})
rErr := NewError(
"no-playing-track",
"No track playing currently.",
http.StatusNotFound,
err.Error(),
c.Request.URL.String(),
nil,
)

s.logger.ErrorwContext(ctx, "failed to retrieve current playing track", "error", rErr.Error())
c.AbortWithStatusJSON(http.StatusNotFound, rErr)

return
}

images, err := s.spoty.TrackImages(ctx, track)
if err != nil {
s.logger.ErrorwContext(ctx, errProcessCurrentTrack.Error(), "error", err.Error())
c.AbortWithStatusJSON(
rErr := NewError(
"failed-retrieve-track-images",
"Could not retrieve track images.",
http.StatusInternalServerError,
Error{Error: errProcessCurrentTrack.Error()},
err.Error(),
c.Request.URL.String(),
map[string]any{
"track": track,
},
)

s.logger.ErrorwContext(ctx, "failed to retrieve track images", "error", rErr.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, rErr)

return
}

Expand Down Expand Up @@ -151,7 +166,18 @@ func (s *Server) handleAuthenticate(c *gin.Context) {
// @Router /api/callback [get]
func (s *Server) handleCallback(c *gin.Context) {
if err := s.spoty.SetupNewClient(c.Request); err != nil {
c.AbortWithStatusJSON(http.StatusForbidden, Error{Error: "could not retrieve token"})
rErr := NewError(
"failed-retrieve-token",
"Could not retrieve spotify token.",
http.StatusForbidden,
err.Error(),
c.Request.URL.String(),
nil,
)

ctx := c.Request.Context()
s.logger.ErrorwContext(ctx, "failed to retrieve spotify token", "error", rErr.Error())
c.AbortWithStatusJSON(http.StatusForbidden, rErr)

return
}
Expand Down

0 comments on commit bc609ae

Please sign in to comment.