1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-15 00:15:15 +02:00

Fix unittests

This commit is contained in:
Lee Brown 2019-08-01 16:17:47 -08:00
parent b3d30a019e
commit 1d69ea88a3
32 changed files with 919 additions and 722 deletions

View File

@ -2,6 +2,9 @@ package handlers
import ( import (
"context" "context"
"fmt"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"net/http" "net/http"
"strconv" "strconv"
@ -45,7 +48,7 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
includeArchived = b includeArchived = b
} }
@ -55,7 +58,10 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case account.ErrNotFound: case account.ErrNotFound:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusNotFound))
fmt.Println("HERE!!!!! account.ErrNotFound")
return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
default: default:
return errors.Wrapf(err, "ID: %s", params["id"]) return errors.Wrapf(err, "ID: %s", params["id"])
} }
@ -78,9 +84,10 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /accounts [patch] // @Router /accounts [patch]
func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values)
if !ok { v, err := webcontext.ContextValues(ctx)
return web.NewShutdownError("web value missing from context") if err != nil {
return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, ok := ctx.Value(auth.Key).(auth.Claims)
@ -89,23 +96,23 @@ func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
} }
var req account.AccountUpdateRequest var req account.AccountUpdateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := account.Update(ctx, claims, a.MasterDB, req, v.Now) err = account.Update(ctx, claims, a.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case account.ErrForbidden: case account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s Account: %+v", req.ID, &req) return errors.Wrapf(err, "Id: %s Account: %+v", req.ID, &req)

View File

@ -2,6 +2,8 @@ package handlers
import ( import (
"context" "context"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -51,7 +53,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
if v := r.URL.Query().Get("where"); v != "" { if v := r.URL.Query().Get("where"); v != "" {
where, args, err := web.ExtractWhereArgs(v) where, args, err := web.ExtractWhereArgs(v)
if err != nil { if err != nil {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.Where = &where req.Where = &where
req.Args = args req.Args = args
@ -72,7 +74,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v) err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -83,7 +85,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v) err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -94,14 +96,14 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.IncludedArchived = b req.IncludedArchived = b
} }
//if err := web.Decode(r, &req); err != nil { //if err := web.Decode(r, &req); err != nil {
// if _, ok := errors.Cause(err).(*web.Error); !ok { // if _, ok := errors.Cause(err).(*web.Error); !ok {
// err = web.NewRequestError(err, http.StatusBadRequest) // err = weberror.NewError(ctx, err, http.StatusBadRequest)
// } // }
// return web.RespondJsonError(ctx, w, err) // return web.RespondJsonError(ctx, w, err)
//} //}
@ -144,7 +146,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
includeArchived = b includeArchived = b
} }
@ -154,7 +156,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case project.ErrNotFound: case project.ErrNotFound:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusNotFound)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
default: default:
return errors.Wrapf(err, "ID: %s", params["id"]) return errors.Wrapf(err, "ID: %s", params["id"])
} }
@ -178,20 +180,20 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /projects [post] // @Router /projects [post]
func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req project.ProjectCreateRequest var req project.ProjectCreateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
@ -201,11 +203,11 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case project.ErrForbidden: case project.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Project: %+v", &req) return errors.Wrapf(err, "Project: %+v", &req)
} }
@ -228,34 +230,34 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /projects [patch] // @Router /projects [patch]
func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req project.ProjectUpdateRequest var req project.ProjectUpdateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := project.Update(ctx, claims, p.MasterDB, req, v.Now) err = project.Update(ctx, claims, p.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case project.ErrForbidden: case project.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "ID: %s Update: %+v", req.ID, req) return errors.Wrapf(err, "ID: %s Update: %+v", req.ID, req)
@ -279,34 +281,34 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /projects/archive [patch] // @Router /projects/archive [patch]
func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req project.ProjectArchiveRequest var req project.ProjectArchiveRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := project.Archive(ctx, claims, p.MasterDB, req, v.Now) err = project.Archive(ctx, claims, p.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case project.ErrForbidden: case project.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s", req.ID) return errors.Wrapf(err, "Id: %s", req.ID)
@ -330,21 +332,21 @@ func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Re
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /projects/{id} [delete] // @Router /projects/{id} [delete]
func (p *Project) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (p *Project) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
err := project.Delete(ctx, claims, p.MasterDB, params["id"]) err = project.Delete(ctx, claims, p.MasterDB, params["id"])
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case project.ErrForbidden: case project.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s", params["id"]) return errors.Wrapf(err, "Id: %s", params["id"])

View File

@ -2,6 +2,8 @@ package handlers
import ( import (
"context" "context"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"net/http" "net/http"
"geeks-accelerator/oss/saas-starter-kit/internal/account" "geeks-accelerator/oss/saas-starter-kit/internal/account"
@ -32,18 +34,18 @@ type Signup struct {
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /signup [post] // @Router /signup [post]
func (c *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (c *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
// Claims are optional as authentication is not required ATM for this method. // Claims are optional as authentication is not required ATM for this method.
claims, _ := ctx.Value(auth.Key).(auth.Claims) claims, _ := auth.ClaimsFromContext(ctx)
var req signup.SignupRequest var req signup.SignupRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
@ -52,11 +54,11 @@ func (c *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Requ
if err != nil { if err != nil {
switch errors.Cause(err) { switch errors.Cause(err) {
case account.ErrForbidden: case account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := err.(validator.ValidationErrors) _, ok := err.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Signup: %+v", &req) return errors.Wrapf(err, "Signup: %+v", &req)

View File

@ -2,6 +2,8 @@ package handlers
import ( import (
"context" "context"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -55,7 +57,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
if v := r.URL.Query().Get("where"); v != "" { if v := r.URL.Query().Get("where"); v != "" {
where, args, err := web.ExtractWhereArgs(v) where, args, err := web.ExtractWhereArgs(v)
if err != nil { if err != nil {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.Where = &where req.Where = &where
req.Args = args req.Args = args
@ -76,7 +78,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v) err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -87,7 +89,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v) err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -98,14 +100,14 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.IncludedArchived = b req.IncludedArchived = b
} }
//if err := web.Decode(r, &req); err != nil { //if err := web.Decode(r, &req); err != nil {
// if _, ok := errors.Cause(err).(*web.Error); !ok { // if _, ok := errors.Cause(err).(*web.Error); !ok {
// err = web.NewRequestError(err, http.StatusBadRequest) // err = weberror.NewError(ctx, err, http.StatusBadRequest)
// } // }
// return web.RespondJsonError(ctx, w, err) // return web.RespondJsonError(ctx, w, err)
//} //}
@ -148,7 +150,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request,
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
includeArchived = b includeArchived = b
} }
@ -158,7 +160,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request,
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrNotFound: case user.ErrNotFound:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusNotFound)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
default: default:
return errors.Wrapf(err, "ID: %s", params["id"]) return errors.Wrapf(err, "ID: %s", params["id"])
} }
@ -181,20 +183,20 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request,
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users [post] // @Router /users [post]
func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user.UserCreateRequest var req user.UserCreateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
@ -204,11 +206,11 @@ func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Reques
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrForbidden: case user.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "User: %+v", &req) return errors.Wrapf(err, "User: %+v", &req)
@ -232,34 +234,34 @@ func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Reques
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users [patch] // @Router /users [patch]
func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user.UserUpdateRequest var req user.UserUpdateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user.Update(ctx, claims, u.MasterDB, req, v.Now) err = user.Update(ctx, claims, u.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrForbidden: case user.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s User: %+v", req.ID, &req) return errors.Wrapf(err, "Id: %s User: %+v", req.ID, &req)
@ -283,36 +285,36 @@ func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Reques
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users/password [patch] // @Router /users/password [patch]
func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user.UserUpdatePasswordRequest var req user.UserUpdatePasswordRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user.UpdatePassword(ctx, claims, u.MasterDB, req, v.Now) err = user.UpdatePassword(ctx, claims, u.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrNotFound: case user.ErrNotFound:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusNotFound)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
case user.ErrForbidden: case user.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s User: %+v", req.ID, &req) return errors.Wrapf(err, "Id: %s User: %+v", req.ID, &req)
@ -336,34 +338,34 @@ func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *htt
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users/archive [patch] // @Router /users/archive [patch]
func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user.UserArchiveRequest var req user.UserArchiveRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user.Archive(ctx, claims, u.MasterDB, req, v.Now) err = user.Archive(ctx, claims, u.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrForbidden: case user.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s", req.ID) return errors.Wrapf(err, "Id: %s", req.ID)
@ -387,21 +389,21 @@ func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Reque
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users/{id} [delete] // @Router /users/{id} [delete]
func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
err := user.Delete(ctx, claims, u.MasterDB, params["id"]) err = user.Delete(ctx, claims, u.MasterDB, params["id"])
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrForbidden: case user.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "Id: %s", params["id"]) return errors.Wrapf(err, "Id: %s", params["id"])
@ -425,14 +427,14 @@ func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Reques
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /users/switch-account/{account_id} [patch] // @Router /users/switch-account/{account_id} [patch]
func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
tkn, err := user.SwitchAccount(ctx, u.MasterDB, u.TokenGenerator, claims, params["account_id"], sessionTtl, v.Now) tkn, err := user.SwitchAccount(ctx, u.MasterDB, u.TokenGenerator, claims, params["account_id"], sessionTtl, v.Now)
@ -440,11 +442,11 @@ func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrAuthenticationFailure: case user.ErrAuthenticationFailure:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusUnauthorized)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusUnauthorized))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrap(err, "switch account") return errors.Wrap(err, "switch account")
@ -468,15 +470,15 @@ func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /oauth/token [post] // @Router /oauth/token [post]
func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
email, pass, ok := r.BasicAuth() email, pass, ok := r.BasicAuth()
if !ok { if !ok {
err := errors.New("must provide email and password in Basic auth") err := errors.New("must provide email and password in Basic auth")
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusUnauthorized)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusUnauthorized))
} }
// Optional to include scope. // Optional to include scope.
@ -487,11 +489,11 @@ func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user.ErrAuthenticationFailure: case user.ErrAuthenticationFailure:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusUnauthorized)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusUnauthorized))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrap(err, "authenticating") return errors.Wrap(err, "authenticating")

View File

@ -4,6 +4,8 @@ import (
"context" "context"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account" "geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -50,7 +52,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
if v := r.URL.Query().Get("where"); v != "" { if v := r.URL.Query().Get("where"); v != "" {
where, args, err := web.ExtractWhereArgs(v) where, args, err := web.ExtractWhereArgs(v)
if err != nil { if err != nil {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.Where = &where req.Where = &where
req.Args = args req.Args = args
@ -71,7 +73,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v) err = errors.WithMessagef(err, "unable to parse %s as int for limit param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -82,7 +84,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
l, err := strconv.Atoi(v) l, err := strconv.Atoi(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v) err = errors.WithMessagef(err, "unable to parse %s as int for offset param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
ul := uint(l) ul := uint(l)
req.Limit = &ul req.Limit = &ul
@ -93,14 +95,14 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
req.IncludedArchived = b req.IncludedArchived = b
} }
//if err := web.Decode(r, &req); err != nil { //if err := web.Decode(r, &req); err != nil {
// if _, ok := errors.Cause(err).(*web.Error); !ok { // if _, ok := errors.Cause(err).(*web.Error); !ok {
// err = web.NewRequestError(err, http.StatusBadRequest) // err = weberror.NewError(ctx, err, http.StatusBadRequest)
// } // }
// return web.RespondJsonError(ctx, w, err) // return web.RespondJsonError(ctx, w, err)
//} //}
@ -143,7 +145,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v) err = errors.WithMessagef(err, "unable to parse %s as boolean for included-archived param", v)
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
includeArchived = b includeArchived = b
} }
@ -153,7 +155,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user_account.ErrNotFound: case user_account.ErrNotFound:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusNotFound)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusNotFound))
default: default:
return errors.Wrapf(err, "ID: %s", params["id"]) return errors.Wrapf(err, "ID: %s", params["id"])
} }
@ -177,20 +179,20 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /user_accounts [post] // @Router /user_accounts [post]
func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user_account.UserAccountCreateRequest var req user_account.UserAccountCreateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
@ -200,11 +202,11 @@ func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user_account.ErrForbidden: case user_account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "User Account: %+v", &req) return errors.Wrapf(err, "User Account: %+v", &req)
@ -228,34 +230,34 @@ func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /user_accounts [patch] // @Router /user_accounts [patch]
func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user_account.UserAccountUpdateRequest var req user_account.UserAccountUpdateRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user_account.Update(ctx, claims, u.MasterDB, req, v.Now) err = user_account.Update(ctx, claims, u.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user_account.ErrForbidden: case user_account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "UserID: %s AccountID: %s User Account: %+v", req.UserID, req.AccountID, &req) return errors.Wrapf(err, "UserID: %s AccountID: %s User Account: %+v", req.UserID, req.AccountID, &req)
@ -279,34 +281,34 @@ func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /user_accounts/archive [patch] // @Router /user_accounts/archive [patch]
func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
v, ok := ctx.Value(web.KeyValues).(*web.Values) v, err := webcontext.ContextValues(ctx)
if !ok { if err != nil {
return web.NewShutdownError("web value missing from context") return err
} }
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user_account.UserAccountArchiveRequest var req user_account.UserAccountArchiveRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user_account.Archive(ctx, claims, u.MasterDB, req, v.Now) err = user_account.Archive(ctx, claims, u.MasterDB, req, v.Now)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user_account.ErrForbidden: case user_account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "UserID: %s AccountID: %s User Account: %+v", req.UserID, req.AccountID, &req) return errors.Wrapf(err, "UserID: %s AccountID: %s User Account: %+v", req.UserID, req.AccountID, &req)
@ -330,29 +332,29 @@ func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *htt
// @Failure 500 {object} web.ErrorResponse // @Failure 500 {object} web.ErrorResponse
// @Router /user_accounts [delete] // @Router /user_accounts [delete]
func (u *UserAccount) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { func (u *UserAccount) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
claims, ok := ctx.Value(auth.Key).(auth.Claims) claims, err := auth.ClaimsFromContext(ctx)
if !ok { if err != nil {
return errors.New("claims missing from context") return err
} }
var req user_account.UserAccountDeleteRequest var req user_account.UserAccountDeleteRequest
if err := web.Decode(r, &req); err != nil { if err := web.Decode(ctx, r, &req); err != nil {
if _, ok := errors.Cause(err).(*web.Error); !ok { if _, ok := errors.Cause(err).(*weberror.Error); !ok {
err = web.NewRequestError(err, http.StatusBadRequest) err = weberror.NewError(ctx, err, http.StatusBadRequest)
} }
return web.RespondJsonError(ctx, w, err) return web.RespondJsonError(ctx, w, err)
} }
err := user_account.Delete(ctx, claims, u.MasterDB, req) err = user_account.Delete(ctx, claims, u.MasterDB, req)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
switch cause { switch cause {
case user_account.ErrForbidden: case user_account.ErrForbidden:
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden))
default: default:
_, ok := cause.(validator.ValidationErrors) _, ok := cause.(validator.ValidationErrors)
if ok { if ok {
return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusBadRequest)) return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusBadRequest))
} }
return errors.Wrapf(err, "UserID: %s, AccountID: %s", req.UserID, req.AccountID) return errors.Wrapf(err, "UserID: %s, AccountID: %s", req.UserID, req.AccountID)

View File

@ -443,7 +443,7 @@ func main() {
if cfg.HTTP.Host != "" { if cfg.HTTP.Host != "" {
api := http.Server{ api := http.Server{
Addr: cfg.HTTP.Host, Addr: cfg.HTTP.Host,
Handler: handlers.API(shutdown, log, masterDb, redisClient, authenticator, serviceMiddlewares...), Handler: handlers.API(shutdown, log, cfg.Env, masterDb, redisClient, authenticator, serviceMiddlewares...),
ReadTimeout: cfg.HTTP.ReadTimeout, ReadTimeout: cfg.HTTP.ReadTimeout,
WriteTimeout: cfg.HTTP.WriteTimeout, WriteTimeout: cfg.HTTP.WriteTimeout,
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
@ -460,7 +460,7 @@ func main() {
if cfg.HTTPS.Host != "" { if cfg.HTTPS.Host != "" {
api := http.Server{ api := http.Server{
Addr: cfg.HTTPS.Host, Addr: cfg.HTTPS.Host,
Handler: handlers.API(shutdown, log, masterDb, redisClient, authenticator, serviceMiddlewares...), Handler: handlers.API(shutdown, log, cfg.Env, masterDb, redisClient, authenticator, serviceMiddlewares...),
ReadTimeout: cfg.HTTPS.ReadTimeout, ReadTimeout: cfg.HTTPS.ReadTimeout,
WriteTimeout: cfg.HTTPS.WriteTimeout, WriteTimeout: cfg.HTTPS.WriteTimeout,
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"net/http" "net/http"
"testing" "testing"
@ -115,7 +116,7 @@ func TestAccountCRUDAdmin(t *testing.T) {
t.Fatalf("\t%s\tDecode expected failed.", tests.Failed) t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected result.", tests.Failed) t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected result.", tests.Success) t.Logf("\t%s\tReceived expected result.", tests.Success)
@ -144,17 +145,17 @@ func TestAccountCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("account %s not found: Entity not found", randID), Error: fmt.Sprintf("account %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -182,17 +183,17 @@ func TestAccountCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("account %s not found: Entity not found", tr.ForbiddenAccount.ID), Error: fmt.Sprintf("account %s not found: Entity not found", tr.ForbiddenAccount.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -329,7 +330,7 @@ func TestAccountCRUDUser(t *testing.T) {
t.Fatalf("\t%s\tDecode expected failed.", tests.Failed) t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected result.", tests.Failed) t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected result.", tests.Success) t.Logf("\t%s\tReceived expected result.", tests.Success)
@ -358,17 +359,17 @@ func TestAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("account %s not found: Entity not found", randID), Error: fmt.Sprintf("account %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -396,17 +397,17 @@ func TestAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("account %s not found: Entity not found", tr.ForbiddenAccount.ID), Error: fmt.Sprintf("account %s not found: Entity not found", tr.ForbiddenAccount.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -438,17 +439,14 @@ func TestAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(), if diff := cmpDiff(t, expected, actual); diff {
}
if diff := cmpDiff(t, actual, expected); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -490,20 +488,27 @@ func TestAccountUpdate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "status", Error: "Key: 'AccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, //{Field: "status", Error: "Key: 'AccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
Tag: "oneof",
Error: "status must be one of [active pending disabled]",
Display: "status must be one of [active pending disabled]",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)

View File

@ -12,6 +12,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests" "geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/project" "geeks-accelerator/oss/saas-starter-kit/internal/project"
"github.com/pborman/uuid" "github.com/pborman/uuid"
) )
@ -90,7 +91,7 @@ func TestProjectCRUDAdmin(t *testing.T) {
t.Fatalf("\t%s\tDecode expected failed.", tests.Failed) t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
if len(expectedMap) == 0 { if len(expectedMap) == 0 {
printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
} }
@ -156,17 +157,17 @@ func TestProjectCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("project %s not found: Entity not found", randID), Error: fmt.Sprintf("project %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -195,17 +196,17 @@ func TestProjectCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID), Error: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -340,17 +341,15 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -416,17 +415,17 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("project %s not found: Entity not found", randID), Error: fmt.Sprintf("project %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -455,17 +454,17 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID), Error: fmt.Sprintf("project %s not found: Entity not found", forbiddenProject.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -497,17 +496,15 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -537,17 +534,15 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -575,17 +570,15 @@ func TestProjectCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -626,20 +619,27 @@ func TestProjectCreate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "status", Error: "Key: 'ProjectCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, //{Field: "status", Error: "Key: 'ProjectCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
Tag: "oneof",
Error: "status must be one of [active disabled]",
Display: "status must be one of [active disabled]",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -681,20 +681,27 @@ func TestProjectUpdate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "status", Error: "Key: 'ProjectUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, //{Field: "status", Error: "Key: 'ProjectUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
Tag: "oneof",
Error: "status must be one of [active disabled]",
Display: "status must be one of [active disabled]",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -716,12 +723,14 @@ func TestProjectArchive(t *testing.T) {
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
invalidId := "a"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch, http.MethodPatch,
"/v1/projects/archive", "/v1/projects/archive",
project.ProjectArchiveRequest{ project.ProjectArchiveRequest{
ID: "a", ID: invalidId,
}, },
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -736,20 +745,27 @@ func TestProjectArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "id", Error: "Key: 'ProjectArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"}, //{Field: "id", Error: "Key: 'ProjectArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"},
{
Field: "id",
Value: invalidId,
Tag: "uuid",
Error: "id must be a valid UUID",
Display: "id must be a valid UUID",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -779,17 +795,17 @@ func TestProjectArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: project.ErrForbidden.Error(), Error: project.ErrForbidden.Error(),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -811,10 +827,12 @@ func TestProjectDelete(t *testing.T) {
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
invalidId := "a"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodDelete, http.MethodDelete,
"/v1/projects/a", "/v1/projects/" + invalidId,
nil, nil,
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -829,20 +847,27 @@ func TestProjectDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"}, //{Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"},
{
Field: "id",
Value: invalidId,
Tag: "uuid",
Error: "id must be a valid UUID",
Display: "id must be a valid UUID",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -870,17 +895,17 @@ func TestProjectDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: project.ErrForbidden.Error(), Error: project.ErrForbidden.Error(),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)

View File

@ -12,6 +12,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests" "geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/signup" "geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "geeks-accelerator/oss/saas-starter-kit/internal/user"
"github.com/pborman/uuid" "github.com/pborman/uuid"
@ -37,7 +38,8 @@ func mockSignupRequest() signup.SignupRequest {
Zipcode: "99686", Zipcode: "99686",
}, },
User: signup.SignupUser{ User: signup.SignupUser{
Name: "Lee Brown", FirstName: "Lee",
LastName: "Brown",
Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Email: uuid.NewRandom().String() + "@geeksinthewoods.com",
Password: "akTechFr0n!ier", Password: "akTechFr0n!ier",
PasswordConfirm: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier",
@ -114,7 +116,8 @@ func TestSignup(t *testing.T) {
expectedMap := map[string]interface{}{ expectedMap := map[string]interface{}{
"user": map[string]interface{}{ "user": map[string]interface{}{
"id": actual.User.ID, "id": actual.User.ID,
"name": req.User.Name, "first_name": req.User.FirstName,
"last_name": req.User.LastName,
"email": req.User.Email, "email": req.User.Email,
"timezone": actual.User.Timezone, "timezone": actual.User.Timezone,
"created_at": web.NewTimeResponse(ctx, actual.User.CreatedAt.Value), "created_at": web.NewTimeResponse(ctx, actual.User.CreatedAt.Value),
@ -149,7 +152,7 @@ func TestSignup(t *testing.T) {
t.Fatalf("\t%s\tDecode expected failed.", tests.Failed) t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
if len(expectedMap) == 0 { if len(expectedMap) == 0 {
printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
} }
@ -180,17 +183,17 @@ func TestSignup(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "decode request body failed: EOF", Error: "decode request body failed",
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -221,21 +224,36 @@ func TestSignup(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "name", Error: "Key: 'SignupRequest.account.name' Error:Field validation for 'name' failed on the 'required' tag"}, //{Field: "name", Error: "Key: 'SignupRequest.account.name' Error:Field validation for 'name' failed on the 'required' tag"},
{Field: "email", Error: "Key: 'SignupRequest.user.email' Error:Field validation for 'email' failed on the 'required' tag"}, //{Field: "email", Error: "Key: 'SignupRequest.user.email' Error:Field validation for 'email' failed on the 'required' tag"},
{
Field: "name",
Value: "",
Tag: "required",
Error: "Name is a required field",
Display: "Name is a required field",
},
{
Field: "email",
Value: "",
Tag: "required",
Error: "email is a required field",
Display: "email is a required field",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -19,6 +20,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests" "geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/signup" "geeks-accelerator/oss/saas-starter-kit/internal/signup"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account" "geeks-accelerator/oss/saas-starter-kit/internal/user_account"
@ -81,7 +83,7 @@ func testMain(m *testing.M) int {
log := test.Log log := test.Log
log.SetOutput(ioutil.Discard) log.SetOutput(ioutil.Discard)
a = handlers.API(shutdown, log, test.MasterDB, nil, authenticator) a = handlers.API(shutdown, log, webcontext.Env_Dev, test.MasterDB, nil, authenticator)
// Create a new account directly business logic. This creates an // Create a new account directly business logic. This creates an
// initial account and user that we will use for admin validated endpoints. // initial account and user that we will use for admin validated endpoints.
@ -122,7 +124,8 @@ func testMain(m *testing.M) int {
// Create a regular user to use when calling regular validated endpoints. // Create a regular user to use when calling regular validated endpoints.
userReq := user.UserCreateRequest{ userReq := user.UserCreateRequest{
Name: "Lucas Brown", FirstName: "Lucas",
LastName: "Brown",
Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Email: uuid.NewRandom().String() + "@geeksinthewoods.com",
Password: "akTechFr0n!ier", Password: "akTechFr0n!ier",
PasswordConfirm: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier",
@ -204,7 +207,7 @@ func executeRequestTest(t *testing.T, tt requestTest, ctx context.Context) (*htt
} }
if tt.error != nil { if tt.error != nil {
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tBody : %s\n", w.Body.String()) t.Logf("\t\tBody : %s\n", w.Body.String())
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)

View File

@ -13,6 +13,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests" "geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account" "geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"github.com/pborman/uuid" "github.com/pborman/uuid"
@ -105,7 +106,7 @@ func TestUserAccountCRUDAdmin(t *testing.T) {
t.Fatalf("\t%s\tDecode expected failed.", tests.Failed) t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
if len(expectedMap) == 0 { if len(expectedMap) == 0 {
printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
} }
@ -171,17 +172,17 @@ func TestUserAccountCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user account %s not found: Entity not found", randID), Error: fmt.Sprintf("user account %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -210,17 +211,17 @@ func TestUserAccountCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user account %s not found: Entity not found", forbiddenUserAccount.ID), Error: fmt.Sprintf("user account %s not found: Entity not found", forbiddenUserAccount.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -363,17 +364,15 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -439,17 +438,17 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user account %s not found: Entity not found", randID), Error: fmt.Sprintf("user account %s not found: Entity not found", randID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -478,17 +477,17 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user account %s not found: Entity not found", forbiddenUserAccount.ID), Error: fmt.Sprintf("user account %s not found: Entity not found", forbiddenUserAccount.ID),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -521,17 +520,17 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: account.ErrForbidden.Error(), Error: account.ErrForbidden.Error(),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -562,17 +561,15 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -603,17 +600,15 @@ func TestUserAccountCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -657,20 +652,27 @@ func TestUserAccountCreate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "status", Error: "Key: 'UserAccountCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, //{Field: "status", Error: "Key: 'UserAccountCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
Tag: "oneof",
Error: "status must be one of [active invited disabled]",
Display: "status must be one of [active invited disabled]",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -713,20 +715,27 @@ func TestUserAccountUpdate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "status", Error: "Key: 'UserAccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, //{Field: "status", Error: "Key: 'UserAccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"},
{
Field: "status",
Value: invalidStatus.String(),
Tag: "oneof",
Error: "status must be one of [active invited disabled]",
Display: "status must be one of [active invited disabled]",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -745,15 +754,15 @@ func TestUserAccountArchive(t *testing.T) {
// Test archive with invalid data. // Test archive with invalid data.
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
req := user_account.UserAccountArchiveRequest{
UserID: "foo",
AccountID: "bar",
}
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch, http.MethodPatch,
"/v1/user_accounts/archive", "/v1/user_accounts/archive",
user_account.UserAccountArchiveRequest{ req,
UserID: "foo",
AccountID: "bar",
},
tr.Token, tr.Token,
tr.Claims, tr.Claims,
expectedStatus, expectedStatus,
@ -767,21 +776,35 @@ func TestUserAccountArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "user_id", Error: "Key: 'UserAccountArchiveRequest.user_id' Error:Field validation for 'user_id' failed on the 'uuid' tag"}, //{Field: "user_id", Error: "Key: 'UserAccountArchiveRequest.user_id' Error:Field validation for 'user_id' failed on the 'uuid' tag"},
{Field: "account_id", Error: "Key: 'UserAccountArchiveRequest.account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"}, //{Field: "account_id", Error: "Key: 'UserAccountArchiveRequest.account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"},
{
Field: "user_id",
Value: req.UserID,
Tag: "uuid",
Error: "user_id must be a valid UUID",
Display: "user_id must be a valid UUID",
},
{
Field: "account_id",
Value: req.AccountID,
Tag: "uuid",
Error: "account_id must be a valid UUID",
Display: "account_id must be a valid UUID",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -813,17 +836,17 @@ func TestUserAccountArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user_account.ErrForbidden.Error(), Error: user_account.ErrForbidden.Error(),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -866,21 +889,35 @@ func TestUserAccountDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "user_id", Error: "Key: 'UserAccountDeleteRequest.user_id' Error:Field validation for 'user_id' failed on the 'uuid' tag"}, //{Field: "user_id", Error: "Key: 'UserAccountDeleteRequest.user_id' Error:Field validation for 'user_id' failed on the 'uuid' tag"},
{Field: "account_id", Error: "Key: 'UserAccountDeleteRequest.account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"}, //{Field: "account_id", Error: "Key: 'UserAccountDeleteRequest.account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"},
{
Field: "user_id",
Value: req.UserID,
Tag: "uuid",
Error: "user_id must be a valid UUID",
Display: "user_id must be a valid UUID",
},
{
Field: "account_id",
Value: req.AccountID,
Tag: "uuid",
Error: "account_id must be a valid UUID",
Display: "account_id must be a valid UUID",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -912,17 +949,17 @@ func TestUserAccountDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user_account.ErrForbidden.Error(), Error: user_account.ErrForbidden.Error(),
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)

View File

@ -13,6 +13,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests" "geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"geeks-accelerator/oss/saas-starter-kit/internal/user" "geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account" "geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"github.com/pborman/uuid" "github.com/pborman/uuid"
@ -25,7 +26,8 @@ type mockUser struct {
func mockUserCreateRequest() user.UserCreateRequest { func mockUserCreateRequest() user.UserCreateRequest {
return user.UserCreateRequest{ return user.UserCreateRequest{
Name: "Lee Brown", FirstName: "Lee",
LastName: "Brown",
Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Email: uuid.NewRandom().String() + "@geeksinthewoods.com",
Password: "akTechFr0n!ier", Password: "akTechFr0n!ier",
PasswordConfirm: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier",
@ -101,7 +103,8 @@ func TestUserCRUDAdmin(t *testing.T) {
"email": req.Email, "email": req.Email,
"timezone": actual.Timezone, "timezone": actual.Timezone,
"created_at": web.NewTimeResponse(ctx, actual.CreatedAt.Value), "created_at": web.NewTimeResponse(ctx, actual.CreatedAt.Value),
"name": req.Name, "first_name": req.FirstName,
"last_name": req.LastName,
} }
var expected user.UserResponse var expected user.UserResponse
@ -187,13 +190,13 @@ func TestUserCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user %s not found: Entity not found", randID), Error: fmt.Sprintf("user %s not found: Entity not found", randID),
} }
@ -225,13 +228,13 @@ func TestUserCRUDAdmin(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID), Error: fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID),
} }
@ -251,8 +254,8 @@ func TestUserCRUDAdmin(t *testing.T) {
http.MethodPatch, http.MethodPatch,
"/v1/users", "/v1/users",
user.UserUpdateRequest{ user.UserUpdateRequest{
ID: created.ID, ID: created.ID,
Name: &newName, FirstName: &newName,
}, },
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -415,6 +418,7 @@ func TestUserCRUDAdmin(t *testing.T) {
"access_token": actual["access_token"], "access_token": actual["access_token"],
"token_type": actual["token_type"], "token_type": actual["token_type"],
"expiry": actual["expiry"], "expiry": actual["expiry"],
"ttl": actual["ttl"],
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {
@ -471,15 +475,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
@ -547,13 +549,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user %s not found: Entity not found", randID), Error: fmt.Sprintf("user %s not found: Entity not found", randID),
} }
@ -585,13 +587,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID), Error: fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID),
} }
@ -611,8 +613,8 @@ func TestUserCRUDUser(t *testing.T) {
http.MethodPatch, http.MethodPatch,
"/v1/users", "/v1/users",
user.UserUpdateRequest{ user.UserUpdateRequest{
ID: created.ID, ID: created.ID,
Name: &newName, FirstName: &newName,
}, },
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -627,13 +629,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrForbidden.Error(), Error: user.ErrForbidden.Error(),
} }
@ -670,13 +672,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrForbidden.Error(), Error: user.ErrForbidden.Error(),
} }
@ -710,15 +712,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
@ -748,15 +748,13 @@ func TestUserCRUDUser(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx)
Error: mid.ErrForbidden.Error(),
}
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
@ -807,6 +805,7 @@ func TestUserCRUDUser(t *testing.T) {
"access_token": actual["access_token"], "access_token": actual["access_token"],
"token_type": actual["token_type"], "token_type": actual["token_type"],
"expiry": actual["expiry"], "expiry": actual["expiry"],
"ttl": actual["ttl"],
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {
@ -864,16 +863,23 @@ func TestUserCreate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "email", Error: "Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"}, //{Field: "email", Error: "Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"},
{
Field: "email",
Value: req.Email,
Tag: "email",
Error: "email must be a valid email address",
Display: "email must be a valid email address",
},
}, },
} }
@ -919,16 +925,23 @@ func TestUserUpdate(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "email", Error: "Key: 'UserUpdateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"}, //{Field: "email", Error: "Key: 'UserUpdateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"},
{
Field: "email",
Value: invalidEmail,
Tag: "email",
Error: "email must be a valid email address",
Display: "email must be a valid email address",
},
}, },
} }
@ -956,6 +969,8 @@ func TestUserUpdatePassword(t *testing.T) {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
newPass := uuid.NewRandom().String() newPass := uuid.NewRandom().String()
diffPass := "different"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Update password %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Update password %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch, http.MethodPatch,
@ -963,7 +978,7 @@ func TestUserUpdatePassword(t *testing.T) {
user.UserUpdatePasswordRequest{ user.UserUpdatePasswordRequest{
ID: created.ID, ID: created.ID,
Password: newPass, Password: newPass,
PasswordConfirm: "different", PasswordConfirm: diffPass,
}, },
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -978,16 +993,23 @@ func TestUserUpdatePassword(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "password_confirm", Error: "Key: 'UserUpdatePasswordRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'eqfield' tag"}, //{Field: "password_confirm", Error: "Key: 'UserUpdatePasswordRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'eqfield' tag"},
{
Field: "password_confirm",
Value: diffPass,
Tag: "eqfield",
Error: "password_confirm must be equal to Password",
Display: "password_confirm must be equal to Password",
},
}, },
} }
@ -1011,12 +1033,14 @@ func TestUserArchive(t *testing.T) {
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
invalidId := "a"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch, http.MethodPatch,
"/v1/users/archive", "/v1/users/archive",
user.UserArchiveRequest{ user.UserArchiveRequest{
ID: "a", ID: invalidId,
}, },
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -1031,16 +1055,23 @@ func TestUserArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "id", Error: "Key: 'UserArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"}, //{Field: "id", Error: "Key: 'UserArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"},
{
Field: "id",
Value: invalidId,
Tag: "uuid",
Error: "id must be a valid UUID",
Display: "id must be a valid UUID",
},
}, },
} }
@ -1074,13 +1105,13 @@ func TestUserArchive(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrForbidden.Error(), Error: user.ErrForbidden.Error(),
} }
@ -1104,10 +1135,12 @@ func TestUserDelete(t *testing.T) {
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
invalidId := "345345"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodDelete, http.MethodDelete,
"/v1/users/345345", fmt.Sprintf("/v1/users/%s", invalidId),
nil, nil,
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -1122,16 +1155,23 @@ func TestUserDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"}, //{Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"},
{
Field: "id",
Value: invalidId,
Tag: "uuid",
Error: "id must be a valid UUID",
Display: "id must be a valid UUID",
},
}, },
} }
@ -1163,13 +1203,13 @@ func TestUserDelete(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrForbidden.Error(), Error: user.ErrForbidden.Error(),
} }
@ -1193,10 +1233,12 @@ func TestUserSwitchAccount(t *testing.T) {
{ {
expectedStatus := http.StatusBadRequest expectedStatus := http.StatusBadRequest
invalidAccountId := "sf"
rt := requestTest{ rt := requestTest{
fmt.Sprintf("Switch account %d w/role %s using invalid data", expectedStatus, tr.Role), fmt.Sprintf("Switch account %d w/role %s using invalid data", expectedStatus, tr.Role),
http.MethodPatch, http.MethodPatch,
"/v1/users/switch-account/sf", "/v1/users/switch-account/" + invalidAccountId,
nil, nil,
tr.Token, tr.Token,
tr.Claims, tr.Claims,
@ -1211,20 +1253,26 @@ func TestUserSwitchAccount(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "field validation error", Error: "Field validation error",
Fields: []web.FieldError{ Fields: []weberror.FieldError{
{Field: "account_id", Error: "Key: 'account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"}, {
Field: "account_id",
Value: invalidAccountId,
Tag: "uuid",
Error: "account_id must be a valid UUID",
Display: "account_id must be a valid UUID",
},
}, },
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, expected, actual); diff {
t.Fatalf("\t%s\tReceived expected error.", tests.Failed) t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
} }
t.Logf("\t%s\tReceived expected error.", tests.Success) t.Logf("\t%s\tReceived expected error.", tests.Success)
@ -1252,13 +1300,13 @@ func TestUserSwitchAccount(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrAuthenticationFailure.Error(), Error: user.ErrAuthenticationFailure.Error(),
} }
@ -1295,13 +1343,13 @@ func TestUserToken(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: "must provide email and password in Basic auth", Error: "must provide email and password in Basic auth",
} }
@ -1342,13 +1390,13 @@ func TestUserToken(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrAuthenticationFailure.Error(), Error: user.ErrAuthenticationFailure.Error(),
} }
@ -1390,13 +1438,13 @@ func TestUserToken(t *testing.T) {
} }
t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code) t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)
var actual web.ErrorResponse var actual weberror.ErrorResponse
if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
t.Logf("\t\tGot error : %+v", err) t.Logf("\t\tGot error : %+v", err)
t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
} }
expected := web.ErrorResponse{ expected := weberror.ErrorResponse{
Error: user.ErrAuthenticationFailure.Error(), Error: user.ErrAuthenticationFailure.Error(),
} }
@ -1450,6 +1498,7 @@ func TestUserToken(t *testing.T) {
"access_token": actual["access_token"], "access_token": actual["access_token"],
"token_type": actual["token_type"], "token_type": actual["token_type"],
"expiry": actual["expiry"], "expiry": actual["expiry"],
"ttl": actual["ttl"],
} }
if diff := cmpDiff(t, actual, expected); diff { if diff := cmpDiff(t, actual, expected); diff {

View File

@ -18,7 +18,7 @@ import (
) )
const ( const (
tmplLayoutBase = "base.tmpl" tmplLayoutBase = "base.gohtml"
tmplContentErrorGeneric = "error-generic.gohtml" tmplContentErrorGeneric = "error-generic.gohtml"
) )

View File

@ -2,11 +2,11 @@ package handlers
import ( import (
"context" "context"
"geeks-accelerator/oss/saas-starter-kit/internal/geonames"
"net/http" "net/http"
"time" "time"
"geeks-accelerator/oss/saas-starter-kit/internal/account" "geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/geonames"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth" "geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
@ -104,5 +104,5 @@ func (h *Signup) Step1(ctx context.Context, w http.ResponseWriter, r *http.Reque
data["validationDefaults"] = verr.(*weberror.Error) data["validationDefaults"] = verr.(*weberror.Error)
} }
return h.Renderer.Render(ctx, w, r, tmplLayoutBase, "signup-step1.tmpl", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data) return h.Renderer.Render(ctx, w, r, tmplLayoutBase, "signup-step1.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
} }

View File

@ -107,7 +107,7 @@ func (h *User) Login(ctx context.Context, w http.ResponseWriter, r *http.Request
data["validationDefaults"] = verr.(*weberror.Error) data["validationDefaults"] = verr.(*weberror.Error)
} }
return h.Renderer.Render(ctx, w, r, tmplLayoutBase, "user-login.tmpl", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data) return h.Renderer.Render(ctx, w, r, tmplLayoutBase, "user-login.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
} }
// handleSessionToken persists the access token to the session for request authentication. // handleSessionToken persists the access token to the session for request authentication.

View File

@ -12,7 +12,6 @@ import (
"github.com/pborman/uuid" "github.com/pborman/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/go-playground/validator.v9"
) )
const ( const (
@ -262,23 +261,17 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
span, ctx := tracer.StartSpanFromContext(ctx, "internal.account.Create") span, ctx := tracer.StartSpanFromContext(ctx, "internal.account.Create")
defer span.Finish() defer span.Finish()
// Validation email address is unique in the database. v := webcontext.Validator()
// Validation account name is unique in the database.
uniq, err := UniqueName(ctx, dbConn, req.Name, "") uniq, err := UniqueName(ctx, dbConn, req.Name, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
f := func(fl validator.FieldLevel) bool { ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq)
if fl.Field().String() == "invalid" {
return false
}
return uniq
}
v := webcontext.Validator()
v.RegisterValidation("unique", f)
// Validate the request. // Validate the request.
err = v.Struct(req) err = v.StructCtx(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -371,24 +364,19 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
v := webcontext.Validator() v := webcontext.Validator()
// Validation name is unique in the database.
if req.Name != nil { if req.Name != nil {
// Validation account name is unique in the database.
uniq, err := UniqueName(ctx, dbConn, *req.Name, req.ID) uniq, err := UniqueName(ctx, dbConn, *req.Name, req.ID)
if err != nil { if err != nil {
return err return err
} }
f := func(fl validator.FieldLevel) bool { ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq)
if fl.Field().String() == "invalid" { } else {
return false ctx = context.WithValue(ctx, webcontext.KeyTagUnique, true)
}
return uniq
}
v.RegisterValidation("unique", f)
} }
// Validate the request. // Validate the request.
err := v.Struct(req) err := v.StructCtx(ctx, req)
if err != nil { if err != nil {
return err return err
} }

View File

@ -32,7 +32,7 @@ func testMain(m *testing.M) int {
// TestFindRequestQuery validates findRequestQuery // TestFindRequestQuery validates findRequestQuery
func TestFindRequestQuery(t *testing.T) { func TestFindRequestQuery(t *testing.T) {
where := "name = ? or address1 = ?" where := "first_name = ? or address1 = ?"
var ( var (
limit uint = 12 limit uint = 12
offset uint = 34 offset uint = 34
@ -41,7 +41,7 @@ func TestFindRequestQuery(t *testing.T) {
req := AccountFindRequest{ req := AccountFindRequest{
Where: &where, Where: &where,
Args: []interface{}{ Args: []interface{}{
"lee brown", "lee",
"103 East Main St.", "103 East Main St.",
}, },
Order: []string{ Order: []string{
@ -51,7 +51,7 @@ func TestFindRequestQuery(t *testing.T) {
Limit: &limit, Limit: &limit,
Offset: &offset, Offset: &offset,
} }
expected := "SELECT " + accountMapColumns + " FROM " + accountTableName + " WHERE (name = ? or address1 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34" expected := "SELECT " + accountMapColumns + " FROM " + accountTableName + " WHERE (first_name = ? or address1 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34"
res, args := findRequestQuery(req) res, args := findRequestQuery(req)
@ -223,7 +223,8 @@ func TestCreateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
@ -293,8 +294,11 @@ func TestCreateValidationNameUnique(t *testing.T) {
t.Fatalf("\t%s\tCreate failed.", tests.Failed) t.Fatalf("\t%s\tCreate failed.", tests.Failed)
} }
if err.Error() != expectedErr.Error() { errStr := strings.Replace(err.Error(), "{{", "", -1)
t.Logf("\t\tGot : %+v", err) errStr = strings.Replace(errStr, "}}", "", -1)
if errStr != expectedErr.Error() {
t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tCreate failed.", tests.Failed) t.Fatalf("\t%s\tCreate failed.", tests.Failed)
} }
@ -431,7 +435,8 @@ func TestUpdateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
@ -501,8 +506,11 @@ func TestUpdateValidationNameUnique(t *testing.T) {
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} }
if err.Error() != expectedErr.Error() { errStr := strings.Replace(err.Error(), "{{", "", -1)
t.Logf("\t\tGot : %+v", err) errStr = strings.Replace(errStr, "}}", "", -1)
if errStr != expectedErr.Error() {
t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} }

View File

@ -3,6 +3,7 @@ package saasSwagger
import ( import (
"context" "context"
"fmt" "fmt"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"html/template" "html/template"
"net/http" "net/http"
@ -74,7 +75,7 @@ func SaasWrapHandler(confs ...func(c *Config)) web.Handler {
prefix = matches[1] prefix = matches[1]
} else if len(matches) > 0 { } else if len(matches) > 0 {
err := errors.WithMessagef(ErrNotFound, "page %s not found", r.RequestURI) err := errors.WithMessagef(ErrNotFound, "page %s not found", r.RequestURI)
return web.NewRequestError(err, http.StatusNotFound) return weberror.NewError(ctx, err, http.StatusNotFound)
} }
// Default to index page. // Default to index page.
@ -92,7 +93,7 @@ func SaasWrapHandler(confs ...func(c *Config)) web.Handler {
case "doc.json": case "doc.json":
doc, err := swag.ReadDoc() doc, err := swag.ReadDoc()
if err != nil { if err != nil {
return web.NewRequestError(err, http.StatusInternalServerError) return weberror.NewError(ctx, err, http.StatusInternalServerError)
} }
// Replace the dynamic placeholder {RANDOM_UUID} // Replace the dynamic placeholder {RANDOM_UUID}

View File

@ -3,6 +3,7 @@ package tests
import ( import (
"context" "context"
"fmt" "fmt"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
"io" "io"
"log" "log"
"os" "os"
@ -12,7 +13,6 @@ import (
"time" "time"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/docker" "geeks-accelerator/oss/saas-starter-kit/internal/platform/docker"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/internal/schema" "geeks-accelerator/oss/saas-starter-kit/internal/schema"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@ -94,7 +94,7 @@ func New() *Test {
} }
// Execute the migrations // Execute the migrations
if err = schema.Migrate(masterDB, log); err != nil { if err = schema.Migrate(masterDB, log, true); err != nil {
log.Fatalf("main : Migrate : %v", err) log.Fatalf("main : Migrate : %v", err)
} }
log.Printf("main : Migrate : Completed") log.Printf("main : Migrate : Completed")
@ -126,10 +126,10 @@ func Recover(t *testing.T) {
// Context returns an app level context for testing. // Context returns an app level context for testing.
func Context() context.Context { func Context() context.Context {
values := web.Values{ values := webcontext.Values{
TraceID: uint64(time.Now().UnixNano()), TraceID: uint64(time.Now().UnixNano()),
Now: time.Now(), Now: time.Now(),
} }
return context.WithValue(context.Background(), web.KeyValues, &values) return context.WithValue(context.Background(), webcontext.KeyValues, &values)
} }

View File

@ -31,7 +31,7 @@ const (
) )
// RespondJsonError sends an error formatted as JSON response back to the client. // RespondJsonError sends an error formatted as JSON response back to the client.
func RespondJsonError(ctx context.Context, w http.ResponseWriter, err error) error { func RespondJsonError(ctx context.Context, w http.ResponseWriter, er error) error {
// Set the status code for the request logger middleware. // Set the status code for the request logger middleware.
// If the context is missing this value, request the service // If the context is missing this value, request the service
@ -41,9 +41,13 @@ func RespondJsonError(ctx context.Context, w http.ResponseWriter, err error) err
return err return err
} }
// If the error was of the type *Error, the handler has webErr, ok := er.(*weberror.Error)
// a specific status code and error to return. if !ok {
webErr := weberror.NewError(ctx, err, v.StatusCode).(*weberror.Error) // If the error was of the type *Error, the handler has
// a specific status code and error to return.
webErr = weberror.NewError(ctx, er, v.StatusCode).(*weberror.Error)
}
v.StatusCode = webErr.Status v.StatusCode = webErr.Status
return RespondJson(ctx, w, webErr.Display(ctx), webErr.Status) return RespondJson(ctx, w, webErr.Display(ctx), webErr.Status)

View File

@ -34,7 +34,10 @@ func ContextWithTranslator(ctx context.Context, translator ut.Translator) contex
// ContextTranslator returns the universal context from a context. // ContextTranslator returns the universal context from a context.
func ContextTranslator(ctx context.Context) ut.Translator { func ContextTranslator(ctx context.Context) ut.Translator {
return ctx.Value(KeyTranslate).(ut.Translator) if t, ok := ctx.Value(KeyTranslate).(ut.Translator); ok {
return t
}
return uniTrans.GetFallback()
} }
// validate holds the settings and caches for validating request struct values. // validate holds the settings and caches for validating request struct values.
@ -122,6 +125,10 @@ func init() {
} }
type ctxKeyTagUnique int
const KeyTagUnique ctxKeyTagUnique = 1
// newValidator inits a new validator with custom settings. // newValidator inits a new validator with custom settings.
func newValidator() *validator.Validate { func newValidator() *validator.Validate {
var v = validator.New() var v = validator.New()
@ -139,10 +146,18 @@ func newValidator() *validator.Validate {
// Empty method that can be overwritten in business logic packages to prevent web.Decode from failing. // Empty method that can be overwritten in business logic packages to prevent web.Decode from failing.
f := func(fl validator.FieldLevel) bool { f := func(fl validator.FieldLevel) bool {
return true return false
} }
v.RegisterValidation("unique", f) v.RegisterValidation("unique", f)
fctx := func(ctx context.Context, fl validator.FieldLevel) bool {
if fl.Field().String() == "invalid" {
return false
}
return ctx.Value(KeyTagUnique).(bool)
}
v.RegisterValidationCtx("unique", fctx)
return v return v
} }

View File

@ -2,7 +2,6 @@ package weberror
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"strings" "strings"
@ -44,7 +43,7 @@ func NewValidationError(ctx context.Context, err error) (error, bool) {
fieldErr = strings.Replace(fieldErr, "}}", "", -1) fieldErr = strings.Replace(fieldErr, "}}", "", -1)
field := FieldError{ field := FieldError{
Field: verror.Field(), Field: fieldName,
Value: verror.Value(), Value: verror.Value(),
Tag: verror.Tag(), Tag: verror.Tag(),
Error: fieldErr, Error: fieldErr,
@ -52,12 +51,12 @@ func NewValidationError(ctx context.Context, err error) (error, bool) {
Display: fieldErr, Display: fieldErr,
} }
switch verror.Tag() { //switch verror.Tag() {
case "required": //case "required":
field.Display = fmt.Sprintf("%s is required.", localName) // field.Display = fmt.Sprintf("%s is required.", localName)
case "unique": //case "unique":
field.Display = fmt.Sprintf("%s must be unique.", localName) // field.Display = fmt.Sprintf("%s must be unique.", localName)
} //}
/* /*
fmt.Println("field", field.Error) fmt.Println("field", field.Error)

View File

@ -8,7 +8,7 @@ import (
// initSchema runs before any migrations are executed. This happens when no other migrations // initSchema runs before any migrations are executed. This happens when no other migrations
// have previously been executed. // have previously been executed.
func initSchema(db *sqlx.DB, log *log.Logger) func(*sqlx.DB) error { func initSchema(db *sqlx.DB, log *log.Logger, isUnittest bool) func(*sqlx.DB) error {
f := func(db *sqlx.DB) error { f := func(db *sqlx.DB) error {
return nil return nil
} }

View File

@ -18,7 +18,7 @@ import (
// migrationList returns a list of migrations to be executed. If the id of the // migrationList returns a list of migrations to be executed. If the id of the
// migration already exists in the migrations table it will be skipped. // migration already exists in the migrations table it will be skipped.
func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration { func migrationList(db *sqlx.DB, log *log.Logger, isUnittest bool) []*sqlxmigrate.Migration {
return []*sqlxmigrate.Migration{ return []*sqlxmigrate.Migration{
// Create table users. // Create table users.
{ {
@ -249,18 +249,22 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration {
return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q) return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q)
} }
resChan := make(chan interface{}) if isUnittest {
go geonames.LoadGeonames(context.Background(), resChan)
for r := range resChan { } else {
switch v := r.(type) { resChan := make(chan interface{})
case geonames.Geoname: go geonames.LoadGeonames(context.Background(), resChan)
_, err = stmt.Exec(v.CountryCode, v.PostalCode, v.PlaceName, v.StateName, v.StateCode, v.CountyName, v.CountyCode, v.CommunityName, v.CommunityCode, v.Latitude, v.Longitude, v.Accuracy)
if err != nil { for r := range resChan {
return errors.WithStack(err) switch v := r.(type) {
case geonames.Geoname:
_, err = stmt.Exec(v.CountryCode, v.PostalCode, v.PlaceName, v.StateName, v.StateCode, v.CountyName, v.CountyCode, v.CommunityName, v.CommunityCode, v.Latitude, v.Longitude, v.Accuracy)
if err != nil {
return errors.WithStack(err)
}
case error:
return v
} }
case error:
return v
} }
} }
@ -287,9 +291,35 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration {
ID: "20190731-02d", ID: "20190731-02d",
Migrate: func(tx *sql.Tx) error { Migrate: func(tx *sql.Tx) error {
prep := []string{ schemas := []string{
`DROP TABLE IF EXISTS countryinfo`, // Countries...
`CREATE TABLE countryinfo ( `DROP TABLE IF EXISTS countries`,
`CREATE TABLE countries(
code char(2) not null constraint countries_pkey primary key,
iso_alpha3 char(3),
name character varying(50),
capital character varying(50),
currency_code char(3),
currency_name CHAR(20),
phone character varying(20),
postal_code_format character varying(200),
postal_code_regex character varying(200))`,
}
for _, q := range schemas {
_, err := db.Exec(q)
if err != nil {
return errors.WithMessagef(err, "Failed to execute sql query '%s'", q)
}
}
if isUnittest {
// `insert into countries(code, iso_alpha3, name, capital, currency_code, currency_name, phone, postal_code_format, postal_code_regex)
} else {
prep := []string{
`DROP TABLE IF EXISTS countryinfo`,
`CREATE TABLE countryinfo (
iso_alpha2 char(2), iso_alpha2 char(2),
iso_alpha3 char(3), iso_alpha3 char(3),
iso_numeric integer, iso_numeric integer,
@ -309,38 +339,108 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration {
geonameId int, geonameId int,
neighbours character varying(50), neighbours character varying(50),
equivalent_fips_code character varying(3))`, equivalent_fips_code character varying(3))`,
} }
for _, q := range prep { for _, q := range prep {
_, err := db.Exec(q) _, err := db.Exec(q)
if err != nil {
return errors.WithMessagef(err, "Failed to execute sql query '%s'", q)
}
}
u := "http://download.geonames.org/export/dump/countryInfo.txt"
resp, err := pester.Get(u)
if err != nil { if err != nil {
return errors.WithMessagef(err, "Failed to execute sql query '%s'", q) return errors.WithMessagef(err, "Failed to read country info from '%s'", u)
} }
} defer resp.Body.Close()
u := "http://download.geonames.org/export/dump/countryInfo.txt" scanner := bufio.NewScanner(resp.Body)
resp, err := pester.Get(u) var prevLine string
if err != nil { var stmt *sql.Stmt
return errors.WithMessagef(err, "Failed to read country info from '%s'", u) for scanner.Scan() {
} line := scanner.Text()
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body) // Skip comments.
var prevLine string if strings.HasPrefix(line, "#") {
var stmt *sql.Stmt prevLine = line
for scanner.Scan() { continue
line := scanner.Text() }
// Skip comments. // Pull the last comment to load the fields.
if strings.HasPrefix(line, "#") { if stmt == nil {
prevLine = line prevLine = strings.TrimPrefix(prevLine, "#")
continue r := csv.NewReader(strings.NewReader(prevLine))
} r.Comma = '\t' // Use tab-delimited instead of comma <---- here!
r.FieldsPerRecord = -1
// Pull the last comment to load the fields. lines, err := r.ReadAll()
if stmt == nil { if err != nil {
prevLine = strings.TrimPrefix(prevLine, "#") return errors.WithStack(err)
r := csv.NewReader(strings.NewReader(prevLine)) }
var columns []string
for _, fn := range lines[0] {
var cn string
switch fn {
case "ISO":
cn = "iso_alpha2"
case "ISO3":
cn = "iso_alpha3"
case "ISO-Numeric":
cn = "iso_numeric"
case "fips":
cn = "fips_code"
case "Country":
cn = "country"
case "Capital":
cn = "capital"
case "Area(in sq km)":
cn = "areainsqkm"
case "Population":
cn = "population"
case "Continent":
cn = "continent"
case "tld":
cn = "tld"
case "CurrencyCode":
cn = "currency_code"
case "CurrencyName":
cn = "currency_name"
case "Phone":
cn = "phone"
case "Postal Code Format":
cn = "postal_format"
case "Postal Code Regex":
cn = "postal_regex"
case "Languages":
cn = "languages"
case "geonameid":
cn = "geonameId"
case "neighbours":
cn = "neighbours"
case "EquivalentFipsCode":
cn = "equivalent_fips_code"
default:
return errors.Errorf("Failed to map column %s", fn)
}
columns = append(columns, cn)
}
placeholders := []string{}
for i := 0; i < len(columns); i++ {
placeholders = append(placeholders, "?")
}
q := "insert into countryinfo (" + strings.Join(columns, ",") + ") values(" + strings.Join(placeholders, ",") + ")"
q = db.Rebind(q)
stmt, err = db.Prepare(q)
if err != nil {
return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q)
}
}
r := csv.NewReader(strings.NewReader(line))
r.Comma = '\t' // Use tab-delimited instead of comma <---- here! r.Comma = '\t' // Use tab-delimited instead of comma <---- here!
r.FieldsPerRecord = -1 r.FieldsPerRecord = -1
@ -348,117 +448,36 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration {
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
var columns []string
for _, fn := range lines[0] { for _, row := range lines {
var cn string var args []interface{}
switch fn { for _, v := range row {
case "ISO": args = append(args, v)
cn = "iso_alpha2"
case "ISO3":
cn = "iso_alpha3"
case "ISO-Numeric":
cn = "iso_numeric"
case "fips":
cn = "fips_code"
case "Country":
cn = "country"
case "Capital":
cn = "capital"
case "Area(in sq km)":
cn = "areainsqkm"
case "Population":
cn = "population"
case "Continent":
cn = "continent"
case "tld":
cn = "tld"
case "CurrencyCode":
cn = "currency_code"
case "CurrencyName":
cn = "currency_name"
case "Phone":
cn = "phone"
case "Postal Code Format":
cn = "postal_format"
case "Postal Code Regex":
cn = "postal_regex"
case "Languages":
cn = "languages"
case "geonameid":
cn = "geonameId"
case "neighbours":
cn = "neighbours"
case "EquivalentFipsCode":
cn = "equivalent_fips_code"
default:
return errors.Errorf("Failed to map column %s", fn)
} }
columns = append(columns, cn)
}
placeholders := []string{} _, err = stmt.Exec(args...)
for i := 0; i < len(columns); i++ { if err != nil {
placeholders = append(placeholders, "?") return errors.WithStack(err)
} }
q := "insert into countryinfo (" + strings.Join(columns, ",") + ") values(" + strings.Join(placeholders, ",") + ")"
q = db.Rebind(q)
stmt, err = db.Prepare(q)
if err != nil {
return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q)
} }
} }
r := csv.NewReader(strings.NewReader(line)) if err := scanner.Err(); err != nil {
r.Comma = '\t' // Use tab-delimited instead of comma <---- here!
r.FieldsPerRecord = -1
lines, err := r.ReadAll()
if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
for _, row := range lines { queries := []string{
var args []interface{} `insert into countries(code, iso_alpha3, name, capital, currency_code, currency_name, phone, postal_code_format, postal_code_regex)
for _, v := range row {
args = append(args, v)
}
_, err = stmt.Exec(args...)
if err != nil {
return errors.WithStack(err)
}
}
}
if err := scanner.Err(); err != nil {
return errors.WithStack(err)
}
queries := []string{
// Countries...
`DROP TABLE IF EXISTS countries`,
`CREATE TABLE countries(
code char(2) not null constraint countries_pkey primary key,
iso_alpha3 char(3),
name character varying(50),
capital character varying(50),
currency_code char(3),
currency_name CHAR(20),
phone character varying(20),
postal_code_format character varying(200),
postal_code_regex character varying(200))`,
`insert into countries(code, iso_alpha3, name, capital, currency_code, currency_name, phone, postal_code_format, postal_code_regex)
select iso_alpha2, iso_alpha3, country, capital, currency_code, currency_name, phone, postal_format, postal_regex select iso_alpha2, iso_alpha3, country, capital, currency_code, currency_name, phone, postal_format, postal_regex
from countryinfo`, from countryinfo`,
`DROP TABLE IF EXISTS countryinfo`, `DROP TABLE IF EXISTS countryinfo`,
} }
for _, q := range queries { for _, q := range queries {
_, err := db.Exec(q) _, err := db.Exec(q)
if err != nil { if err != nil {
return errors.WithMessagef(err, "Failed to execute sql query '%s'", q) return errors.WithMessagef(err, "Failed to execute sql query '%s'", q)
}
} }
} }
@ -488,48 +507,52 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration {
} }
} }
u := "http://download.geonames.org/export/dump/timeZones.txt" if isUnittest {
resp, err := pester.Get(u)
if err != nil {
return errors.WithMessagef(err, "Failed to read timezones info from '%s'", u)
}
defer resp.Body.Close()
q := "insert into country_timezones (country_code,timezone_id) values(?, ?)" } else {
q = db.Rebind(q) u := "http://download.geonames.org/export/dump/timeZones.txt"
stmt, err := db.Prepare(q) resp, err := pester.Get(u)
if err != nil {
return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q)
}
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
// Skip comments.
if strings.HasPrefix(line, "CountryCode") {
continue
}
r := csv.NewReader(strings.NewReader(line))
r.Comma = '\t' // Use tab-delimited instead of comma <---- here!
r.FieldsPerRecord = -1
lines, err := r.ReadAll()
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithMessagef(err, "Failed to read timezones info from '%s'", u)
}
defer resp.Body.Close()
q := "insert into country_timezones (country_code,timezone_id) values(?, ?)"
q = db.Rebind(q)
stmt, err := db.Prepare(q)
if err != nil {
return errors.WithMessagef(err, "Failed to prepare sql query '%s'", q)
} }
for _, row := range lines { scanner := bufio.NewScanner(resp.Body)
_, err = stmt.Exec(row[0], row[1]) for scanner.Scan() {
line := scanner.Text()
// Skip comments.
if strings.HasPrefix(line, "CountryCode") {
continue
}
r := csv.NewReader(strings.NewReader(line))
r.Comma = '\t' // Use tab-delimited instead of comma <---- here!
r.FieldsPerRecord = -1
lines, err := r.ReadAll()
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
}
}
if err := scanner.Err(); err != nil { for _, row := range lines {
return errors.WithStack(err) _, err = stmt.Exec(row[0], row[1])
if err != nil {
return errors.WithStack(err)
}
}
}
if err := scanner.Err(); err != nil {
return errors.WithStack(err)
}
} }
return nil return nil

View File

@ -7,15 +7,15 @@ import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
func Migrate(masterDb *sqlx.DB, log *log.Logger) error { func Migrate(masterDb *sqlx.DB, log *log.Logger, isUnittest bool) error {
// Load list of Schema migrations and init new sqlxmigrate client // Load list of Schema migrations and init new sqlxmigrate client
migrations := migrationList(masterDb, log) migrations := migrationList(masterDb, log, isUnittest)
m := sqlxmigrate.New(masterDb, sqlxmigrate.DefaultOptions, migrations) m := sqlxmigrate.New(masterDb, sqlxmigrate.DefaultOptions, migrations)
m.SetLogger(log) m.SetLogger(log)
// Append any schema that need to be applied if this is a fresh migration // Append any schema that need to be applied if this is a fresh migration
// ie. the migrations database table does not exist. // ie. the migrations database table does not exist.
m.InitSchema(initSchema(masterDb, log)) m.InitSchema(initSchema(masterDb, log, isUnittest))
// Execute the migrations // Execute the migrations
return m.Migrate() return m.Migrate()

View File

@ -13,7 +13,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/go-playground/validator.v9"
) )
const ( const (
@ -278,23 +277,17 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserCr
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user.Create") span, ctx := tracer.StartSpanFromContext(ctx, "internal.user.Create")
defer span.Finish() defer span.Finish()
v := webcontext.Validator()
// Validation email address is unique in the database. // Validation email address is unique in the database.
uniq, err := UniqueEmail(ctx, dbConn, req.Email, "") uniq, err := UniqueEmail(ctx, dbConn, req.Email, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
f := func(fl validator.FieldLevel) bool { ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq)
if fl.Field().String() == "invalid" {
return false
}
return uniq
}
v := webcontext.Validator()
v.RegisterValidation("unique", f)
// Validate the request. // Validate the request.
err = v.Struct(req) err = v.StructCtx(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -394,21 +387,18 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserUp
// Validation email address is unique in the database. // Validation email address is unique in the database.
if req.Email != nil { if req.Email != nil {
// Validation email address is unique in the database.
uniq, err := UniqueEmail(ctx, dbConn, *req.Email, req.ID) uniq, err := UniqueEmail(ctx, dbConn, *req.Email, req.ID)
if err != nil { if err != nil {
return err return err
} }
f := func(fl validator.FieldLevel) bool { ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq)
if fl.Field().String() == "invalid" { } else {
return false ctx = context.WithValue(ctx, webcontext.KeyTagUnique, true)
}
return uniq
}
v.RegisterValidation("unique", f)
} }
// Validate the request. // Validate the request.
err := v.Struct(req) err := v.StructCtx(ctx, req)
if err != nil { if err != nil {
return err return err
} }
@ -437,10 +427,10 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserUp
var fields []string var fields []string
if req.FirstName != nil { if req.FirstName != nil {
fields = append(fields, query.Assign("name", req.FirstName)) fields = append(fields, query.Assign("first_name", req.FirstName))
} }
if req.LastName != nil { if req.LastName != nil {
fields = append(fields, query.Assign("name", req.LastName)) fields = append(fields, query.Assign("last_name", req.LastName))
} }
if req.Email != nil { if req.Email != nil {
fields = append(fields, query.Assign("email", req.Email)) fields = append(fields, query.Assign("email", req.Email))

View File

@ -152,7 +152,8 @@ func TestCreateValidation(t *testing.T) {
errors.New("Key: 'UserCreateRequest.first_name' Error:Field validation for 'first_name' failed on the 'required' tag\n" + errors.New("Key: 'UserCreateRequest.first_name' Error:Field validation for 'first_name' failed on the 'required' tag\n" +
"Key: 'UserCreateRequest.last_name' Error:Field validation for 'last_name' failed on the 'required' tag\n" + "Key: 'UserCreateRequest.last_name' Error:Field validation for 'last_name' failed on the 'required' tag\n" +
"Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'required' tag\n" + "Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'required' tag\n" +
"Key: 'UserCreateRequest.password' Error:Field validation for 'password' failed on the 'required' tag"), "Key: 'UserCreateRequest.password' Error:Field validation for 'password' failed on the 'required' tag\n" +
"Key: 'UserCreateRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'required' tag"),
}, },
{"Valid Email", {"Valid Email",
UserCreateRequest{ UserCreateRequest{
@ -224,15 +225,16 @@ func TestCreateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
expectStr = tt.error.Error() expectStr = tt.error.Error()
} }
if errStr != expectStr { if errStr != expectStr {
t.Logf("\t\tGot : %+v", err) t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", tt.error) t.Logf("\t\tWant: %+v", expectStr)
t.Fatalf("\t%s\tCreate failed.", tests.Failed) t.Fatalf("\t%s\tCreate failed.", tests.Failed)
} }
} }
@ -290,8 +292,11 @@ func TestCreateValidationEmailUnique(t *testing.T) {
t.Fatalf("\t%s\tCreate failed.", tests.Failed) t.Fatalf("\t%s\tCreate failed.", tests.Failed)
} }
if err.Error() != expectedErr.Error() { errStr := strings.Replace(err.Error(), "{{", "", -1)
t.Logf("\t\tGot : %+v", err) errStr = strings.Replace(errStr, "}}", "", -1)
if errStr != expectedErr.Error() {
t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tCreate failed.", tests.Failed) t.Fatalf("\t%s\tCreate failed.", tests.Failed)
} }
@ -422,15 +427,16 @@ func TestUpdateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
expectStr = tt.error.Error() expectStr = tt.error.Error()
} }
if errStr != expectStr { if errStr != expectStr {
t.Logf("\t\tGot : %+v", err) t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", tt.error) t.Logf("\t\tWant: %+v", expectStr)
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} }
} }
@ -488,8 +494,11 @@ func TestUpdateValidationEmailUnique(t *testing.T) {
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} }
if err.Error() != expectedErr.Error() { errStr := strings.Replace(err.Error(), "{{", "", -1)
t.Logf("\t\tGot : %+v", err) errStr = strings.Replace(errStr, "}}", "", -1)
if errStr != expectedErr.Error() {
t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} }
@ -547,13 +556,19 @@ func TestUpdatePassword(t *testing.T) {
// Ensure validation is working by trying UpdatePassword with an empty request. // Ensure validation is working by trying UpdatePassword with an empty request.
expectedErr := errors.New("Key: 'UserUpdatePasswordRequest.id' Error:Field validation for 'id' failed on the 'required' tag\n" + expectedErr := errors.New("Key: 'UserUpdatePasswordRequest.id' Error:Field validation for 'id' failed on the 'required' tag\n" +
"Key: 'UserUpdatePasswordRequest.password' Error:Field validation for 'password' failed on the 'required' tag") "Key: 'UserUpdatePasswordRequest.password' Error:Field validation for 'password' failed on the 'required' tag\n" +
"Key: 'UserUpdatePasswordRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'required' tag")
err = UpdatePassword(ctx, auth.Claims{}, test.MasterDB, UserUpdatePasswordRequest{}, now) err = UpdatePassword(ctx, auth.Claims{}, test.MasterDB, UserUpdatePasswordRequest{}, now)
if err == nil { if err == nil {
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tUpdate failed.", tests.Failed) t.Fatalf("\t%s\tUpdate failed.", tests.Failed)
} else if err.Error() != expectedErr.Error() { }
t.Logf("\t\tGot : %+v", err)
errStr := strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
if errStr != expectedErr.Error() {
t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", expectedErr) t.Logf("\t\tWant: %+v", expectedErr)
t.Fatalf("\t%s\tValidation failed.", tests.Failed) t.Fatalf("\t%s\tValidation failed.", tests.Failed)
} }

View File

@ -232,15 +232,16 @@ func TestCreateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
expectStr = tt.error.Error() expectStr = tt.error.Error()
} }
if errStr != expectStr { if errStr != expectStr {
t.Logf("\t\tGot : %+v", err) t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", tt.error) t.Logf("\t\tWant: %+v", expectStr)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed) t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
} }
} }
@ -411,15 +412,16 @@ func TestUpdateValidation(t *testing.T) {
// of type interface validator.ValidationErrorsTranslations // of type interface validator.ValidationErrorsTranslations
var errStr string var errStr string
if err != nil { if err != nil {
errStr = err.Error() errStr = strings.Replace(err.Error(), "{{", "", -1)
errStr = strings.Replace(errStr, "}}", "", -1)
} }
var expectStr string var expectStr string
if tt.error != nil { if tt.error != nil {
expectStr = tt.error.Error() expectStr = tt.error.Error()
} }
if errStr != expectStr { if errStr != expectStr {
t.Logf("\t\tGot : %+v", err) t.Logf("\t\tGot : %+v", errStr)
t.Logf("\t\tWant: %+v", tt.error) t.Logf("\t\tWant: %+v", expectStr)
t.Fatalf("\t%s\tUpdate user account failed.", tests.Failed) t.Fatalf("\t%s\tUpdate user account failed.", tests.Failed)
} }
} }

View File

@ -126,7 +126,7 @@ func main() {
// Start Migrations // Start Migrations
// Execute the migrations // Execute the migrations
if err = schema.Migrate(masterDb, log); err != nil { if err = schema.Migrate(masterDb, log, false); err != nil {
log.Fatalf("main : Migrate : %v", err) log.Fatalf("main : Migrate : %v", err)
} }
log.Printf("main : Migrate : Completed") log.Printf("main : Migrate : Completed")