Skip to content

Commit

Permalink
✨ feat: api router
Browse files Browse the repository at this point in the history
  • Loading branch information
perebaj committed Sep 29, 2023
1 parent 80a7b9c commit 8dd1fc2
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 56 deletions.
20 changes: 20 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (

"log/slog"

"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
"github.com/perebaj/contractus"
)

Expand Down Expand Up @@ -93,3 +95,21 @@ func convert(content, email string) (t []contractus.Transaction, err error) {
}
return t, nil
}

// Router gather all the routes of the API.
func Router(a Auth, storage transactionStorage) http.Handler {
r := chi.NewRouter()
r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(a.JWTAuth()))
r.Use(jwtauth.Authenticator)

RegisterTransactionsHandler(r, storage)
})

r.Group(func(r chi.Router) {
// Public routes
RegisterAuthHandler(r, a)
RegisterSwaggerHandler(r)
})
return r
}
1 change: 1 addition & 0 deletions api/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Response:
{
"transactions": [
{
"email": "[email protected]",
"type": "string",
"date": "2023-09-27T17:34:41.455Z",
"product_description": "string",
Expand Down
3 changes: 3 additions & 0 deletions api/docs/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ components:
Transaction:
type: object
properties:
email:
type: string
description: User email
type:
type: string
description: Transaction type
Expand Down
13 changes: 9 additions & 4 deletions api/transactionhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func RegisterSwaggerHandler(r chi.Router) {
func (s transactionHandler) producerBalance(w http.ResponseWriter, r *http.Request) {
email, err := emailFromRequest(r)
if err != nil {
sendErr(w, http.StatusBadRequest, Error{"email_required", "email is required"})
sendErr(w, http.StatusUnauthorized, Error{"user unauthorized", "user unauthorized"})
return
}

Expand All @@ -92,7 +92,7 @@ func (s transactionHandler) producerBalance(w http.ResponseWriter, r *http.Reque
func (s transactionHandler) affiliateBalance(w http.ResponseWriter, r *http.Request) {
email, err := emailFromRequest(r)
if err != nil {
sendErr(w, http.StatusBadRequest, Error{"email_required", "email is required"})
sendErr(w, http.StatusUnauthorized, Error{"unauthorized_request", "unauthoriz"})
return
}
query := r.URL.Query()
Expand All @@ -118,7 +118,7 @@ func (s transactionHandler) affiliateBalance(w http.ResponseWriter, r *http.Requ
func (s transactionHandler) upload(w http.ResponseWriter, r *http.Request) {
email, err := emailFromRequest(r)
if err != nil {
sendErr(w, http.StatusBadRequest, Error{"email_required", "email is required"})
sendErr(w, http.StatusUnauthorized, Error{"unauthorized_request", "unauthorized request"})
return
}

Expand Down Expand Up @@ -148,7 +148,7 @@ func (s transactionHandler) upload(w http.ResponseWriter, r *http.Request) {
func (s transactionHandler) transactions(w http.ResponseWriter, r *http.Request) {
email, err := emailFromRequest(r)
if err != nil {
sendErr(w, http.StatusBadRequest, Error{"email_required", "email is required"})
sendErr(w, http.StatusUnauthorized, Error{"unauthorized_request", "unauthoriz"})
return
}

Expand Down Expand Up @@ -222,5 +222,10 @@ func emailFromRequest(r *http.Request) (string, error) {
if err != nil {
return "", fmt.Errorf("failed to get claims from context: %v", err)
}

if claims["email"] == "" {
return "", fmt.Errorf("failed to get email from claims: %v", err)
}

return claims["email"].(string), nil
}
100 changes: 64 additions & 36 deletions api/transactionhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,9 @@ func TestTransactionHandlerUpload(t *testing.T) {
t.Fatal(err)
}
m := &mockTransactionStorage{}
r := chi.NewRouter()

tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
_, token, _ := tokenAuth.Encode(map[string]interface{}{"email": "[email protected]"})

r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
r.Use(jwtauth.Authenticator)
RegisterTransactionsHandler(r, m)
})
token, authConfig := authTest(t)
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodPost, "/upload", &b)
req.Header.Set("Content-Type", w.FormDataContentType())
Expand All @@ -73,17 +66,24 @@ func TestTransactionHandlerUpload(t *testing.T) {
}
}

func TestTransactionHandlerBalanceProducer(t *testing.T) {
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
_, token, _ := tokenAuth.Encode(map[string]interface{}{"email": "[email protected]"})
func TestTransactionHandlerUpload_Unauthorized(t *testing.T) {
_, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodPost, "/upload", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)

if resp.Code != http.StatusUnauthorized {
t.Fatalf("expected status code %d, got %d", http.StatusUnauthorized, resp.Code)
}
}

func TestTransactionHandlerBalanceProducer(t *testing.T) {
token, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := chi.NewRouter()
r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
r.Use(jwtauth.Authenticator)
RegisterTransactionsHandler(r, m)
})
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodGet, "/balance/producer?name=JOSE%20CARLOS", nil)
req.AddCookie(&http.Cookie{
Expand All @@ -100,17 +100,24 @@ func TestTransactionHandlerBalanceProducer(t *testing.T) {
}
}

func TestTransactionHandlerBalanceAffiliate(t *testing.T) {
func TestTransactionHandlerBalanceProducer_Unauthorized(t *testing.T) {
_, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := chi.NewRouter()
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
_, token, _ := tokenAuth.Encode(map[string]interface{}{"email": "[email protected]"})
r := Router(authConfig, m)

r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
r.Use(jwtauth.Authenticator)
RegisterTransactionsHandler(r, m)
})
req := httptest.NewRequest(http.MethodGet, "/balance/producer?name=JOSE%20CARLOS", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)

if resp.Code != http.StatusUnauthorized {
t.Fatalf("expected status code %d, got %d", http.StatusUnauthorized, resp.Code)
}
}

func TestTransactionHandlerBalanceAffiliate(t *testing.T) {
token, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodGet, "/balance/affiliate?name=JOSE%20CARLOS", nil)
req.AddCookie(&http.Cookie{
Expand All @@ -128,16 +135,9 @@ func TestTransactionHandlerBalanceAffiliate(t *testing.T) {
}

func TestTransactionHandlerTransactions(t *testing.T) {
token, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := chi.NewRouter()
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
_, token, _ := tokenAuth.Encode(map[string]interface{}{"email": "[email protected]"})

r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
r.Use(jwtauth.Authenticator)
RegisterTransactionsHandler(r, m)
})
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodGet, "/transactions", nil)
req.AddCookie(&http.Cookie{
Expand All @@ -154,6 +154,20 @@ func TestTransactionHandlerTransactions(t *testing.T) {
}
}

func TestTransactionHandlerTransactions_Unauthorized(t *testing.T) {
_, authConfig := authTest(t)
m := &mockTransactionStorage{}
r := Router(authConfig, m)

req := httptest.NewRequest(http.MethodGet, "/transactions", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)

if resp.Code != http.StatusUnauthorized {
t.Fatalf("expected status code %d, got %d", http.StatusUnauthorized, resp.Code)
}
}

func TestSwaggerHandler(t *testing.T) {
r := chi.NewRouter()
RegisterSwaggerHandler(r)
Expand All @@ -175,3 +189,17 @@ func TestSwaggerHandler(t *testing.T) {
t.Fatalf("expected status code %d, got %d", http.StatusOK, resp.Code)
}
}

// authTest is a helper function to generate a JWT token and Auth struct for testing.
func authTest(t *testing.T) (token string, authConfig Auth) {
t.Helper()
secretKey := "secret" //Fake secret Key
tokenAuth := jwtauth.New("HS256", []byte(secretKey), nil)
_, token, _ = tokenAuth.Encode(map[string]interface{}{"email": "[email protected]"})

authConfig = Auth{
JWTSecretKey: secretKey,
}

return token, authConfig
}
17 changes: 1 addition & 16 deletions cmd/contractus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (

"log/slog"

"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
"github.com/perebaj/contractus/api"
"github.com/perebaj/contractus/postgres"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -77,20 +75,7 @@ func main() {

cfg.Auth.GoogleOAuthConfig = &googleOAuthConfig

r := chi.NewRouter()
r.Group(func(r chi.Router) {
// Authenticated routes
r.Use(jwtauth.Verifier(cfg.Auth.JWTAuth()))
r.Use(jwtauth.Authenticator)

api.RegisterTransactionsHandler(r, storage)
})

r.Group(func(r chi.Router) {
// Public routes
api.RegisterAuthHandler(r, cfg.Auth)
api.RegisterSwaggerHandler(r)
})
r := api.Router(cfg.Auth, storage)

svc := &http.Server{
Addr: ":" + cfg.PORT,
Expand Down

0 comments on commit 8dd1fc2

Please sign in to comment.