Skip to content

Commit

Permalink
fix error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
denpeshkov committed Feb 17, 2024
1 parent 1c132e6 commit 5d0a261
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 201 deletions.
16 changes: 8 additions & 8 deletions internal/greenlight/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package greenlight
import (
"context"
"errors"
"fmt"
"net/mail"
"unicode/utf8"

"github.com/denpeshkov/greenlight/internal/multierr"
"golang.org/x/crypto/bcrypt"
)

Expand Down Expand Up @@ -52,16 +52,16 @@ func (u *User) Valid() error {
type Password []byte

// NewPasswords generates a hashed password from the plaintext password.
func NewPassword(plaintext string) (Password, error) {
op := "greenlight.NewPassword"
func NewPassword(plaintext string) (_ Password, err error) {
defer multierr.Wrap(&err, "greenlight.NewPassword")

if err := PasswordValid(plaintext); err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
return nil, err
}

hash, err := bcrypt.GenerateFromPassword([]byte(plaintext), 12)
if err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
return nil, err
}
return hash, nil
}
Expand All @@ -85,15 +85,15 @@ func PasswordValid(plaintext string) error {
}

// Matches tests whether the provided plaintext password matches the hashed password.
func (p *Password) Matches(plaintext string) (bool, error) {
op := "greenlight.password.Matches"
func (p *Password) Matches(plaintext string) (_ bool, err error) {
defer multierr.Wrap(&err, "greenlight.password.Matches")

if err := bcrypt.CompareHashAndPassword(*p, []byte(plaintext)); err != nil {
switch {
case errors.Is(err, bcrypt.ErrMismatchedHashAndPassword):
return false, nil
default:
return false, fmt.Errorf("%s: %w", op, err)
return false, err
}
}
return true, nil
Expand Down
37 changes: 15 additions & 22 deletions internal/http/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,50 @@ package http

import (
"errors"
"fmt"
"net/http"

"github.com/denpeshkov/greenlight/internal/greenlight"
"github.com/denpeshkov/greenlight/internal/multierr"
)

func (s *Server) registerAuthHandlers() {
s.router.HandleFunc("POST /v1/auth/token", s.handleCreateToken)
s.router.Handle("POST /v1/auth/token", s.handlerFunc(s.handleCreateToken))
}

// handleCreateToken handles requests to create an authentication token.
func (s *Server) handleCreateToken(w http.ResponseWriter, r *http.Request) {
op := "http.Server.handleCreateToken"
func (s *Server) handleCreateToken(w http.ResponseWriter, r *http.Request) (err error) {
defer multierr.Wrap(&err, "http.Server.handleCreateToken")

var req struct {
Email string `json:"email"`
Password string `json:"password"`
}
if err := s.readRequest(w, r, &req); err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
if err = s.readRequest(w, r, &req); err != nil {
return err
}

if err := greenlight.PasswordValid(req.Password); err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
if err = greenlight.PasswordValid(req.Password); err != nil {
return err
}

u, err := s.userService.Get(r.Context(), req.Email)
if err != nil {
switch {
case errors.Is(err, greenlight.ErrNotFound):
s.Error(w, r, greenlight.NewUnauthorizedError("Invalid credentials."))
return greenlight.NewUnauthorizedError("Invalid credentials.")
default:
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return err
}
return
}

if match, err := u.Password.Matches(req.Password); err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
return err
} else if !match {
s.Error(w, r, greenlight.NewUnauthorizedError("Invalid credentials."))
return
return greenlight.NewUnauthorizedError("Invalid credentials.")
}

token, err := s.authService.CreateToken(r.Context(), u.ID)
if err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
return err
}

resp := struct {
Expand All @@ -62,7 +55,7 @@ func (s *Server) handleCreateToken(w http.ResponseWriter, r *http.Request) {
}

if err := s.sendResponse(w, r, http.StatusCreated, resp, nil); err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
return err
}
return nil
}
13 changes: 7 additions & 6 deletions internal/http/healtcheck.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
package http

import (
"fmt"
"net/http"

"github.com/denpeshkov/greenlight/internal/multierr"
)

func (s *Server) registerHealthCheckHandlers() {
s.router.HandleFunc("GET /v1/healthcheck", s.handleHealthCheck)
s.router.Handle("GET /v1/healthcheck", s.handlerFunc(s.handleHealthCheck))
}

// handleHealthCheck handles requests to get application information (status).
func (s *Server) handleHealthCheck(w http.ResponseWriter, r *http.Request) {
op := "http.Server.handleHealthCheck"
func (s *Server) handleHealthCheck(w http.ResponseWriter, r *http.Request) (err error) {
defer multierr.Wrap(&err, "http.Server.handleHealthCheck")

info := HealthInfo{"1.0", "UP"}
w.Header().Set("Content-Type", "application/json")
if err := s.sendResponse(w, r, http.StatusOK, info, nil); err != nil {
s.Error(w, r, fmt.Errorf("%s: %w", op, err))
return
return err
}
return nil
}

// Application information.
Expand Down
10 changes: 5 additions & 5 deletions internal/http/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ package http

import (
"encoding/json"
"fmt"
"net/http"
"net/textproto"
"strings"

"github.com/denpeshkov/greenlight/internal/greenlight"
"github.com/denpeshkov/greenlight/internal/multierr"
)

// sendResponse sends a JSON response with a given status.
// In case of an error, response (and status) is not send and error is returned.
func (s *Server) sendResponse(w http.ResponseWriter, r *http.Request, status int, resp any, headers http.Header) error {
op := "http.Server.sendResponse"
func (s *Server) sendResponse(w http.ResponseWriter, r *http.Request, status int, resp any, headers http.Header) (err error) {
defer multierr.Wrap(&err, "http.Server.sendResponse")

js, err := json.Marshal(resp)
if err != nil {
return fmt.Errorf("%s: %w", op, err)
return err
}
for k, v := range headers {
k = textproto.CanonicalMIMEHeaderKey(k)
Expand All @@ -26,7 +26,7 @@ func (s *Server) sendResponse(w http.ResponseWriter, r *http.Request, status int
w.WriteHeader(status)
_, err = w.Write(js)
if err != nil {
return fmt.Errorf("%s: %w", op, err)
return err
}
return nil
}
Expand Down
Loading

0 comments on commit 5d0a261

Please sign in to comment.