diff --git a/cmd/web-api/handlers/account.go b/cmd/web-api/handlers/account.go index bbc1844..76b0cc0 100644 --- a/cmd/web-api/handlers/account.go +++ b/cmd/web-api/handlers/account.go @@ -2,6 +2,9 @@ package handlers import ( "context" + "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "net/http" "strconv" @@ -45,7 +48,7 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque b, err := strconv.ParseBool(v) if err != nil { 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 } @@ -55,7 +58,10 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque cause := errors.Cause(err) switch cause { 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: 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 // @Router /accounts [patch] 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 { - return web.NewShutdownError("web value missing from context") + + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } 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 - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) diff --git a/cmd/web-api/handlers/project.go b/cmd/web-api/handlers/project.go index 9477869..fe53e83 100644 --- a/cmd/web-api/handlers/project.go +++ b/cmd/web-api/handlers/project.go @@ -2,6 +2,8 @@ package handlers import ( "context" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "net/http" "strconv" "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 != "" { where, args, err := web.ExtractWhereArgs(v) 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.Args = args @@ -72,7 +74,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque l, err := strconv.Atoi(v) if err != nil { 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) 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) if err != nil { 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) 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) if err != nil { 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 } //if err := web.Decode(r, &req); err != nil { // 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) //} @@ -144,7 +146,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque b, err := strconv.ParseBool(v) if err != nil { 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 } @@ -154,7 +156,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque cause := errors.Cause(err) switch cause { 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: 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 // @Router /projects [post] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req project.ProjectCreateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) } @@ -228,34 +230,34 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req // @Failure 500 {object} web.ErrorResponse // @Router /projects [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req project.ProjectUpdateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -279,34 +281,34 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req // @Failure 500 {object} web.ErrorResponse // @Router /projects/archive [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req project.ProjectArchiveRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -330,21 +332,21 @@ func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Re // @Failure 500 {object} web.ErrorResponse // @Router /projects/{id} [delete] 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) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } - err := project.Delete(ctx, claims, p.MasterDB, params["id"]) + err = project.Delete(ctx, claims, p.MasterDB, params["id"]) if err != nil { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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"]) diff --git a/cmd/web-api/handlers/signup.go b/cmd/web-api/handlers/signup.go index 37ddaae..46e6de2 100644 --- a/cmd/web-api/handlers/signup.go +++ b/cmd/web-api/handlers/signup.go @@ -2,6 +2,8 @@ package handlers import ( "context" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "net/http" "geeks-accelerator/oss/saas-starter-kit/internal/account" @@ -32,18 +34,18 @@ type Signup struct { // @Failure 500 {object} web.ErrorResponse // @Router /signup [post] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } // 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 - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { switch errors.Cause(err) { 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: _, ok := err.(validator.ValidationErrors) 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) diff --git a/cmd/web-api/handlers/user.go b/cmd/web-api/handlers/user.go index 42f5aef..0807392 100644 --- a/cmd/web-api/handlers/user.go +++ b/cmd/web-api/handlers/user.go @@ -2,6 +2,8 @@ package handlers import ( "context" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "net/http" "strconv" "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 != "" { where, args, err := web.ExtractWhereArgs(v) 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.Args = args @@ -76,7 +78,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, l, err := strconv.Atoi(v) if err != nil { 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) 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) if err != nil { 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) 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) if err != nil { 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 } //if err := web.Decode(r, &req); err != nil { // 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) //} @@ -148,7 +150,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, b, err := strconv.ParseBool(v) if err != nil { 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 } @@ -158,7 +160,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, cause := errors.Cause(err) switch cause { 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: 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 // @Router /users [post] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user.UserCreateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -232,34 +234,34 @@ func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Reques // @Failure 500 {object} web.ErrorResponse // @Router /users [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user.UserUpdateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -283,36 +285,36 @@ func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Reques // @Failure 500 {object} web.ErrorResponse // @Router /users/password [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user.UserUpdatePasswordRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: - return web.RespondJsonError(ctx, w, web.NewRequestError(err, http.StatusForbidden)) + return web.RespondJsonError(ctx, w, weberror.NewError(ctx, err, http.StatusForbidden)) default: _, ok := cause.(validator.ValidationErrors) 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) @@ -336,34 +338,34 @@ func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *htt // @Failure 500 {object} web.ErrorResponse // @Router /users/archive [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user.UserArchiveRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -387,21 +389,21 @@ func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Reque // @Failure 500 {object} web.ErrorResponse // @Router /users/{id} [delete] 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) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } - err := user.Delete(ctx, claims, u.MasterDB, params["id"]) + err = user.Delete(ctx, claims, u.MasterDB, params["id"]) if err != nil { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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"]) @@ -425,14 +427,14 @@ func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Reques // @Failure 500 {object} web.ErrorResponse // @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 { - v, ok := ctx.Value(web.KeyValues).(*web.Values) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } 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) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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") @@ -468,15 +470,15 @@ func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http // @Failure 500 {object} web.ErrorResponse // @Router /oauth/token [post] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } email, pass, ok := r.BasicAuth() if !ok { 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. @@ -487,11 +489,11 @@ func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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") diff --git a/cmd/web-api/handlers/user_account.go b/cmd/web-api/handlers/user_account.go index f020506..7f96f32 100644 --- a/cmd/web-api/handlers/user_account.go +++ b/cmd/web-api/handlers/user_account.go @@ -4,6 +4,8 @@ import ( "context" "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/webcontext" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "geeks-accelerator/oss/saas-starter-kit/internal/user_account" "github.com/jmoiron/sqlx" "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 != "" { where, args, err := web.ExtractWhereArgs(v) 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.Args = args @@ -71,7 +73,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R l, err := strconv.Atoi(v) if err != nil { 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) 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) if err != nil { 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) 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) if err != nil { 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 } //if err := web.Decode(r, &req); err != nil { // 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) //} @@ -143,7 +145,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R b, err := strconv.ParseBool(v) if err != nil { 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 } @@ -153,7 +155,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R cause := errors.Cause(err) switch cause { 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: 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 // @Router /user_accounts [post] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user_account.UserAccountCreateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -228,34 +230,34 @@ func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http // @Failure 500 {object} web.ErrorResponse // @Router /user_accounts [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user_account.UserAccountUpdateRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -279,34 +281,34 @@ func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http // @Failure 500 {object} web.ErrorResponse // @Router /user_accounts/archive [patch] 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) - if !ok { - return web.NewShutdownError("web value missing from context") + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err } - claims, ok := ctx.Value(auth.Key).(auth.Claims) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user_account.UserAccountArchiveRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) @@ -330,29 +332,29 @@ func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *htt // @Failure 500 {object} web.ErrorResponse // @Router /user_accounts [delete] 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) - if !ok { - return errors.New("claims missing from context") + claims, err := auth.ClaimsFromContext(ctx) + if err != nil { + return err } var req user_account.UserAccountDeleteRequest - if err := web.Decode(r, &req); err != nil { - if _, ok := errors.Cause(err).(*web.Error); !ok { - err = web.NewRequestError(err, http.StatusBadRequest) + if err := web.Decode(ctx, r, &req); err != nil { + if _, ok := errors.Cause(err).(*weberror.Error); !ok { + err = weberror.NewError(ctx, err, http.StatusBadRequest) } 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 { cause := errors.Cause(err) switch cause { 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: _, ok := cause.(validator.ValidationErrors) 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) diff --git a/cmd/web-api/main.go b/cmd/web-api/main.go index d3b67aa..98c3622 100644 --- a/cmd/web-api/main.go +++ b/cmd/web-api/main.go @@ -443,7 +443,7 @@ func main() { if cfg.HTTP.Host != "" { api := http.Server{ 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, WriteTimeout: cfg.HTTP.WriteTimeout, MaxHeaderBytes: 1 << 20, @@ -460,7 +460,7 @@ func main() { if cfg.HTTPS.Host != "" { api := http.Server{ 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, WriteTimeout: cfg.HTTPS.WriteTimeout, MaxHeaderBytes: 1 << 20, diff --git a/cmd/web-api/tests/account_test.go b/cmd/web-api/tests/account_test.go index c44f2c4..21fad6f 100644 --- a/cmd/web-api/tests/account_test.go +++ b/cmd/web-api/tests/account_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "net/http" "testing" @@ -115,7 +116,7 @@ func TestAccountCRUDAdmin(t *testing.T) { 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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected result.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } - - if diff := cmpDiff(t, actual, expected); diff { + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "status", Error: "Key: 'AccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.Logf("\t%s\tReceived expected error.", tests.Success) diff --git a/cmd/web-api/tests/project_test.go b/cmd/web-api/tests/project_test.go index 237fc10..14629c9 100644 --- a/cmd/web-api/tests/project_test.go +++ b/cmd/web-api/tests/project_test.go @@ -12,6 +12,7 @@ import ( "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/web" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "geeks-accelerator/oss/saas-starter-kit/internal/project" "github.com/pborman/uuid" ) @@ -90,7 +91,7 @@ func TestProjectCRUDAdmin(t *testing.T) { 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 { 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "status", Error: "Key: 'ProjectCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "status", Error: "Key: 'ProjectUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.Logf("\t%s\tReceived expected error.", tests.Success) @@ -716,12 +723,14 @@ func TestProjectArchive(t *testing.T) { { expectedStatus := http.StatusBadRequest + invalidId := "a" + rt := requestTest{ fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodPatch, "/v1/projects/archive", project.ProjectArchiveRequest{ - ID: "a", + ID: invalidId, }, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "id", Error: "Key: 'ProjectArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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.Logf("\t%s\tReceived expected error.", tests.Success) @@ -811,10 +827,12 @@ func TestProjectDelete(t *testing.T) { { expectedStatus := http.StatusBadRequest + invalidId := "a" + rt := requestTest{ fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodDelete, - "/v1/projects/a", + "/v1/projects/" + invalidId, nil, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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.Logf("\t%s\tReceived expected error.", tests.Success) diff --git a/cmd/web-api/tests/signup_test.go b/cmd/web-api/tests/signup_test.go index 6a6b612..246885b 100644 --- a/cmd/web-api/tests/signup_test.go +++ b/cmd/web-api/tests/signup_test.go @@ -12,6 +12,7 @@ import ( "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/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/user" "github.com/pborman/uuid" @@ -37,7 +38,8 @@ func mockSignupRequest() signup.SignupRequest { Zipcode: "99686", }, User: signup.SignupUser{ - Name: "Lee Brown", + FirstName: "Lee", + LastName: "Brown", Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Password: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier", @@ -114,7 +116,8 @@ func TestSignup(t *testing.T) { expectedMap := map[string]interface{}{ "user": map[string]interface{}{ "id": actual.User.ID, - "name": req.User.Name, + "first_name": req.User.FirstName, + "last_name": req.User.LastName, "email": req.User.Email, "timezone": actual.User.Timezone, "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) } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { if len(expectedMap) == 0 { 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "decode request body failed: EOF", + expected := weberror.ErrorResponse{ + 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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {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"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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: "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.Logf("\t%s\tReceived expected error.", tests.Success) diff --git a/cmd/web-api/tests/tests_test.go b/cmd/web-api/tests/tests_test.go index b12111b..b63b937 100644 --- a/cmd/web-api/tests/tests_test.go +++ b/cmd/web-api/tests/tests_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "io" "io/ioutil" "net/http" @@ -19,6 +20,7 @@ import ( "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/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/user" "geeks-accelerator/oss/saas-starter-kit/internal/user_account" @@ -81,7 +83,7 @@ func testMain(m *testing.M) int { log := test.Log 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 // 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. userReq := user.UserCreateRequest{ - Name: "Lucas Brown", + FirstName: "Lucas", + LastName: "Brown", Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Password: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier", @@ -204,7 +207,7 @@ func executeRequestTest(t *testing.T, tt requestTest, ctx context.Context) (*htt } if tt.error != nil { - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tBody : %s\n", w.Body.String()) t.Logf("\t\tGot error : %+v", err) diff --git a/cmd/web-api/tests/user_account_test.go b/cmd/web-api/tests/user_account_test.go index 6d2eb88..db5d6b8 100644 --- a/cmd/web-api/tests/user_account_test.go +++ b/cmd/web-api/tests/user_account_test.go @@ -13,6 +13,7 @@ import ( "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/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_account" "github.com/pborman/uuid" @@ -105,7 +106,7 @@ func TestUserAccountCRUDAdmin(t *testing.T) { 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 { 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) - if diff := cmpDiff(t, actual, expected); diff { + if diff := cmpDiff(t, expected, actual); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "status", Error: "Key: 'UserAccountCreateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "status", Error: "Key: 'UserAccountUpdateRequest.status' Error:Field validation for 'status' failed on the 'oneof' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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.Logf("\t%s\tReceived expected error.", tests.Success) @@ -745,15 +754,15 @@ func TestUserAccountArchive(t *testing.T) { // Test archive with invalid data. { expectedStatus := http.StatusBadRequest - + req := user_account.UserAccountArchiveRequest{ + UserID: "foo", + AccountID: "bar", + } rt := requestTest{ fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodPatch, "/v1/user_accounts/archive", - user_account.UserAccountArchiveRequest{ - UserID: "foo", - AccountID: "bar", - }, + req, tr.Token, tr.Claims, expectedStatus, @@ -767,21 +776,35 @@ func TestUserAccountArchive(t *testing.T) { } 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 { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {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"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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: "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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {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"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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: "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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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.Logf("\t%s\tReceived expected error.", tests.Success) diff --git a/cmd/web-api/tests/user_test.go b/cmd/web-api/tests/user_test.go index 6000c45..47d7f51 100644 --- a/cmd/web-api/tests/user_test.go +++ b/cmd/web-api/tests/user_test.go @@ -13,6 +13,7 @@ import ( "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/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_account" "github.com/pborman/uuid" @@ -25,7 +26,8 @@ type mockUser struct { func mockUserCreateRequest() user.UserCreateRequest { return user.UserCreateRequest{ - Name: "Lee Brown", + FirstName: "Lee", + LastName: "Brown", Email: uuid.NewRandom().String() + "@geeksinthewoods.com", Password: "akTechFr0n!ier", PasswordConfirm: "akTechFr0n!ier", @@ -101,7 +103,8 @@ func TestUserCRUDAdmin(t *testing.T) { "email": req.Email, "timezone": actual.Timezone, "created_at": web.NewTimeResponse(ctx, actual.CreatedAt.Value), - "name": req.Name, + "first_name": req.FirstName, + "last_name": req.LastName, } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } @@ -225,13 +228,13 @@ func TestUserCRUDAdmin(t *testing.T) { } 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 { t.Logf("\t\tGot error : %+v", err) 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), } @@ -251,8 +254,8 @@ func TestUserCRUDAdmin(t *testing.T) { http.MethodPatch, "/v1/users", user.UserUpdateRequest{ - ID: created.ID, - Name: &newName, + ID: created.ID, + FirstName: &newName, }, tr.Token, tr.Claims, @@ -415,6 +418,7 @@ func TestUserCRUDAdmin(t *testing.T) { "access_token": actual["access_token"], "token_type": actual["token_type"], "expiry": actual["expiry"], + "ttl": actual["ttl"], } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) if diff := cmpDiff(t, actual, expected); diff { 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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), } @@ -585,13 +587,13 @@ func TestUserCRUDUser(t *testing.T) { } 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 { t.Logf("\t\tGot error : %+v", err) 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), } @@ -611,8 +613,8 @@ func TestUserCRUDUser(t *testing.T) { http.MethodPatch, "/v1/users", user.UserUpdateRequest{ - ID: created.ID, - Name: &newName, + ID: created.ID, + FirstName: &newName, }, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) if diff := cmpDiff(t, actual, expected); diff { 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: mid.ErrForbidden.Error(), - } + expected := mid.ErrorForbidden(ctx).(*weberror.Error).Display(ctx) if diff := cmpDiff(t, actual, expected); diff { t.Fatalf("\t%s\tReceived expected error.", tests.Failed) @@ -807,6 +805,7 @@ func TestUserCRUDUser(t *testing.T) { "access_token": actual["access_token"], "token_type": actual["token_type"], "expiry": actual["expiry"], + "ttl": actual["ttl"], } 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "email", Error: "Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "email", Error: "Key: 'UserUpdateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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 newPass := uuid.NewRandom().String() + diffPass := "different" + rt := requestTest{ fmt.Sprintf("Update password %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodPatch, @@ -963,7 +978,7 @@ func TestUserUpdatePassword(t *testing.T) { user.UserUpdatePasswordRequest{ ID: created.ID, Password: newPass, - PasswordConfirm: "different", + PasswordConfirm: diffPass, }, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "password_confirm", Error: "Key: 'UserUpdatePasswordRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'eqfield' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + 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", + 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 + invalidId := "a" + rt := requestTest{ fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodPatch, "/v1/users/archive", user.UserArchiveRequest{ - ID: "a", + ID: invalidId, }, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "id", Error: "Key: 'UserArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ Error: user.ErrForbidden.Error(), } @@ -1104,10 +1135,12 @@ func TestUserDelete(t *testing.T) { { expectedStatus := http.StatusBadRequest + invalidId := "345345" + rt := requestTest{ fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodDelete, - "/v1/users/345345", + fmt.Sprintf("/v1/users/%s", invalidId), nil, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + //{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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ Error: user.ErrForbidden.Error(), } @@ -1193,10 +1233,12 @@ func TestUserSwitchAccount(t *testing.T) { { expectedStatus := http.StatusBadRequest + invalidAccountId := "sf" + rt := requestTest{ fmt.Sprintf("Switch account %d w/role %s using invalid data", expectedStatus, tr.Role), http.MethodPatch, - "/v1/users/switch-account/sf", + "/v1/users/switch-account/" + invalidAccountId, nil, tr.Token, 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ - Error: "field validation error", - Fields: []web.FieldError{ - {Field: "account_id", Error: "Key: 'account_id' Error:Field validation for 'account_id' failed on the 'uuid' tag"}, + expected := weberror.ErrorResponse{ + Error: "Field validation error", + Fields: []weberror.FieldError{ + { + 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.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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) 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", } @@ -1342,13 +1390,13 @@ func TestUserToken(t *testing.T) { } 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 { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ 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) - var actual web.ErrorResponse + var actual weberror.ErrorResponse if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil { t.Logf("\t\tGot error : %+v", err) t.Fatalf("\t%s\tDecode response body failed.", tests.Failed) } - expected := web.ErrorResponse{ + expected := weberror.ErrorResponse{ Error: user.ErrAuthenticationFailure.Error(), } @@ -1450,6 +1498,7 @@ func TestUserToken(t *testing.T) { "access_token": actual["access_token"], "token_type": actual["token_type"], "expiry": actual["expiry"], + "ttl": actual["ttl"], } if diff := cmpDiff(t, actual, expected); diff { diff --git a/cmd/web-app/handlers/routes.go b/cmd/web-app/handlers/routes.go index 528e6d8..8afab02 100644 --- a/cmd/web-app/handlers/routes.go +++ b/cmd/web-app/handlers/routes.go @@ -18,7 +18,7 @@ import ( ) const ( - tmplLayoutBase = "base.tmpl" + tmplLayoutBase = "base.gohtml" tmplContentErrorGeneric = "error-generic.gohtml" ) diff --git a/cmd/web-app/handlers/signup.go b/cmd/web-app/handlers/signup.go index 7dd078f..90bf220 100644 --- a/cmd/web-app/handlers/signup.go +++ b/cmd/web-app/handlers/signup.go @@ -2,11 +2,11 @@ package handlers import ( "context" - "geeks-accelerator/oss/saas-starter-kit/internal/geonames" "net/http" "time" "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/web" "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) } - 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) } diff --git a/cmd/web-app/handlers/user.go b/cmd/web-app/handlers/user.go index d2adb37..3679b5d 100644 --- a/cmd/web-app/handlers/user.go +++ b/cmd/web-app/handlers/user.go @@ -107,7 +107,7 @@ func (h *User) Login(ctx context.Context, w http.ResponseWriter, r *http.Request 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. diff --git a/cmd/web-app/templates/content/signup-step1.tmpl b/cmd/web-app/templates/content/signup-step1.gohtml similarity index 100% rename from cmd/web-app/templates/content/signup-step1.tmpl rename to cmd/web-app/templates/content/signup-step1.gohtml diff --git a/cmd/web-app/templates/content/user-login.tmpl b/cmd/web-app/templates/content/user-login.gohtml similarity index 100% rename from cmd/web-app/templates/content/user-login.tmpl rename to cmd/web-app/templates/content/user-login.gohtml diff --git a/cmd/web-app/templates/layouts/base.tmpl b/cmd/web-app/templates/layouts/base.gohtml similarity index 100% rename from cmd/web-app/templates/layouts/base.tmpl rename to cmd/web-app/templates/layouts/base.gohtml diff --git a/internal/account/account.go b/internal/account/account.go index c363b8b..eabaf61 100644 --- a/internal/account/account.go +++ b/internal/account/account.go @@ -12,7 +12,6 @@ import ( "github.com/pborman/uuid" "github.com/pkg/errors" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "gopkg.in/go-playground/validator.v9" ) 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") 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, "") if err != nil { return nil, err } - f := func(fl validator.FieldLevel) bool { - if fl.Field().String() == "invalid" { - return false - } - return uniq - } - - v := webcontext.Validator() - v.RegisterValidation("unique", f) + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq) // Validate the request. - err = v.Struct(req) + err = v.StructCtx(ctx, req) if err != nil { return nil, err } @@ -371,24 +364,19 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun v := webcontext.Validator() - // Validation name is unique in the database. if req.Name != nil { + // Validation account name is unique in the database. uniq, err := UniqueName(ctx, dbConn, *req.Name, req.ID) if err != nil { return err } - f := func(fl validator.FieldLevel) bool { - if fl.Field().String() == "invalid" { - return false - } - - return uniq - } - v.RegisterValidation("unique", f) + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq) + } else { + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, true) } // Validate the request. - err := v.Struct(req) + err := v.StructCtx(ctx, req) if err != nil { return err } diff --git a/internal/account/account_test.go b/internal/account/account_test.go index db1ae48..ebc9b79 100644 --- a/internal/account/account_test.go +++ b/internal/account/account_test.go @@ -32,7 +32,7 @@ func testMain(m *testing.M) int { // TestFindRequestQuery validates findRequestQuery func TestFindRequestQuery(t *testing.T) { - where := "name = ? or address1 = ?" + where := "first_name = ? or address1 = ?" var ( limit uint = 12 offset uint = 34 @@ -41,7 +41,7 @@ func TestFindRequestQuery(t *testing.T) { req := AccountFindRequest{ Where: &where, Args: []interface{}{ - "lee brown", + "lee", "103 East Main St.", }, Order: []string{ @@ -51,7 +51,7 @@ func TestFindRequestQuery(t *testing.T) { Limit: &limit, 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) @@ -223,7 +223,8 @@ func TestCreateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { @@ -293,8 +294,11 @@ func TestCreateValidationNameUnique(t *testing.T) { t.Fatalf("\t%s\tCreate failed.", tests.Failed) } - 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.Fatalf("\t%s\tCreate failed.", tests.Failed) } @@ -431,7 +435,8 @@ func TestUpdateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { @@ -501,8 +506,11 @@ func TestUpdateValidationNameUnique(t *testing.T) { t.Fatalf("\t%s\tUpdate failed.", tests.Failed) } - 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.Fatalf("\t%s\tUpdate failed.", tests.Failed) } diff --git a/internal/mid/saas-swagger/swagger.go b/internal/mid/saas-swagger/swagger.go index d46f4b2..0acf0ec 100644 --- a/internal/mid/saas-swagger/swagger.go +++ b/internal/mid/saas-swagger/swagger.go @@ -3,6 +3,7 @@ package saasSwagger import ( "context" "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror" "github.com/pborman/uuid" "html/template" "net/http" @@ -74,7 +75,7 @@ func SaasWrapHandler(confs ...func(c *Config)) web.Handler { prefix = matches[1] } else if len(matches) > 0 { 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. @@ -92,7 +93,7 @@ func SaasWrapHandler(confs ...func(c *Config)) web.Handler { case "doc.json": doc, err := swag.ReadDoc() if err != nil { - return web.NewRequestError(err, http.StatusInternalServerError) + return weberror.NewError(ctx, err, http.StatusInternalServerError) } // Replace the dynamic placeholder {RANDOM_UUID} diff --git a/internal/platform/tests/main.go b/internal/platform/tests/main.go index 06ad194..d20e6cf 100644 --- a/internal/platform/tests/main.go +++ b/internal/platform/tests/main.go @@ -3,6 +3,7 @@ package tests import ( "context" "fmt" + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext" "io" "log" "os" @@ -12,7 +13,6 @@ import ( "time" "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" "github.com/aws/aws-sdk-go/aws/session" "github.com/jmoiron/sqlx" @@ -94,7 +94,7 @@ func New() *Test { } // 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.Printf("main : Migrate : Completed") @@ -126,10 +126,10 @@ func Recover(t *testing.T) { // Context returns an app level context for testing. func Context() context.Context { - values := web.Values{ + values := webcontext.Values{ TraceID: uint64(time.Now().UnixNano()), Now: time.Now(), } - return context.WithValue(context.Background(), web.KeyValues, &values) + return context.WithValue(context.Background(), webcontext.KeyValues, &values) } diff --git a/internal/platform/web/response.go b/internal/platform/web/response.go index 90c3c15..9a5310f 100644 --- a/internal/platform/web/response.go +++ b/internal/platform/web/response.go @@ -31,7 +31,7 @@ const ( ) // 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. // 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 } - // If the error was of the type *Error, the handler has - // a specific status code and error to return. - webErr := weberror.NewError(ctx, err, v.StatusCode).(*weberror.Error) + webErr, ok := er.(*weberror.Error) + if !ok { + // 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 return RespondJson(ctx, w, webErr.Display(ctx), webErr.Status) diff --git a/internal/platform/web/webcontext/transvalidate.go b/internal/platform/web/webcontext/transvalidate.go index 1ce4287..d17cf0d 100644 --- a/internal/platform/web/webcontext/transvalidate.go +++ b/internal/platform/web/webcontext/transvalidate.go @@ -34,7 +34,10 @@ func ContextWithTranslator(ctx context.Context, translator ut.Translator) contex // ContextTranslator returns the universal context from a context. 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. @@ -122,6 +125,10 @@ func init() { } +type ctxKeyTagUnique int + +const KeyTagUnique ctxKeyTagUnique = 1 + // newValidator inits a new validator with custom settings. func newValidator() *validator.Validate { 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. f := func(fl validator.FieldLevel) bool { - return true + return false } 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 } diff --git a/internal/platform/web/weberror/validation.go b/internal/platform/web/weberror/validation.go index 69f5684..65d3a54 100644 --- a/internal/platform/web/weberror/validation.go +++ b/internal/platform/web/weberror/validation.go @@ -2,7 +2,6 @@ package weberror import ( "context" - "fmt" "net/http" "strings" @@ -44,7 +43,7 @@ func NewValidationError(ctx context.Context, err error) (error, bool) { fieldErr = strings.Replace(fieldErr, "}}", "", -1) field := FieldError{ - Field: verror.Field(), + Field: fieldName, Value: verror.Value(), Tag: verror.Tag(), Error: fieldErr, @@ -52,12 +51,12 @@ func NewValidationError(ctx context.Context, err error) (error, bool) { Display: fieldErr, } - switch verror.Tag() { - case "required": - field.Display = fmt.Sprintf("%s is required.", localName) - case "unique": - field.Display = fmt.Sprintf("%s must be unique.", localName) - } + //switch verror.Tag() { + //case "required": + // field.Display = fmt.Sprintf("%s is required.", localName) + //case "unique": + // field.Display = fmt.Sprintf("%s must be unique.", localName) + //} /* fmt.Println("field", field.Error) diff --git a/internal/schema/init_schema.go b/internal/schema/init_schema.go index 329ce10..6326de6 100644 --- a/internal/schema/init_schema.go +++ b/internal/schema/init_schema.go @@ -8,7 +8,7 @@ import ( // initSchema runs before any migrations are executed. This happens when no other migrations // 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 { return nil } diff --git a/internal/schema/migrations.go b/internal/schema/migrations.go index bf1ccb0..d098711 100644 --- a/internal/schema/migrations.go +++ b/internal/schema/migrations.go @@ -18,7 +18,7 @@ import ( // 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. -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{ // 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) } - resChan := make(chan interface{}) - go geonames.LoadGeonames(context.Background(), resChan) + if isUnittest { - for r := range resChan { - 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) + } else { + resChan := make(chan interface{}) + go geonames.LoadGeonames(context.Background(), resChan) + + for r := range resChan { + 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", Migrate: func(tx *sql.Tx) error { - prep := []string{ - `DROP TABLE IF EXISTS countryinfo`, - `CREATE TABLE countryinfo ( + schemas := []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))`, + } + + 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_alpha3 char(3), iso_numeric integer, @@ -309,38 +339,108 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration { geonameId int, neighbours character varying(50), equivalent_fips_code character varying(3))`, - } + } - for _, q := range prep { - _, err := db.Exec(q) + for _, q := range prep { + _, 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 { - 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" - resp, err := pester.Get(u) - if err != nil { - return errors.WithMessagef(err, "Failed to read country info from '%s'", u) - } - defer resp.Body.Close() + scanner := bufio.NewScanner(resp.Body) + var prevLine string + var stmt *sql.Stmt + for scanner.Scan() { + line := scanner.Text() - scanner := bufio.NewScanner(resp.Body) - var prevLine string - var stmt *sql.Stmt - for scanner.Scan() { - line := scanner.Text() + // Skip comments. + if strings.HasPrefix(line, "#") { + prevLine = line + continue + } - // Skip comments. - if strings.HasPrefix(line, "#") { - prevLine = line - continue - } + // Pull the last comment to load the fields. + if stmt == nil { + prevLine = strings.TrimPrefix(prevLine, "#") + 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. - if stmt == nil { - prevLine = strings.TrimPrefix(prevLine, "#") - r := csv.NewReader(strings.NewReader(prevLine)) + lines, err := r.ReadAll() + if err != nil { + return errors.WithStack(err) + } + 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.FieldsPerRecord = -1 @@ -348,117 +448,36 @@ func migrationList(db *sqlx.DB, log *log.Logger) []*sqlxmigrate.Migration { if err != nil { return errors.WithStack(err) } - 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) + for _, row := range lines { + var args []interface{} + for _, v := range row { + args = append(args, v) } - 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) + _, err = stmt.Exec(args...) + if err != nil { + return errors.WithStack(err) + } } } - 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 := scanner.Err(); err != nil { return errors.WithStack(err) } - for _, row := range lines { - var args []interface{} - 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) + queries := []string{ + `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 from countryinfo`, - `DROP TABLE IF EXISTS countryinfo`, - } + `DROP TABLE IF EXISTS countryinfo`, + } - for _, q := range queries { - _, err := db.Exec(q) - if err != nil { - return errors.WithMessagef(err, "Failed to execute sql query '%s'", q) + for _, q := range queries { + _, err := db.Exec(q) + if err != nil { + 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" - resp, err := pester.Get(u) - if err != nil { - return errors.WithMessagef(err, "Failed to read timezones info from '%s'", u) - } - defer resp.Body.Close() + if isUnittest { - 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) - } - - 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() + } else { + u := "http://download.geonames.org/export/dump/timeZones.txt" + resp, err := pester.Get(u) 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 { - _, err = stmt.Exec(row[0], row[1]) + 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 { return errors.WithStack(err) } - } - } - if err := scanner.Err(); err != nil { - return errors.WithStack(err) + for _, row := range lines { + _, 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 diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 84ef30f..d4121d4 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -7,15 +7,15 @@ import ( "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 - migrations := migrationList(masterDb, log) + migrations := migrationList(masterDb, log, isUnittest) m := sqlxmigrate.New(masterDb, sqlxmigrate.DefaultOptions, migrations) m.SetLogger(log) // Append any schema that need to be applied if this is a fresh migration // ie. the migrations database table does not exist. - m.InitSchema(initSchema(masterDb, log)) + m.InitSchema(initSchema(masterDb, log, isUnittest)) // Execute the migrations return m.Migrate() diff --git a/internal/user/user.go b/internal/user/user.go index eee12a7..5c3d5af 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -13,7 +13,6 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/bcrypt" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "gopkg.in/go-playground/validator.v9" ) 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") defer span.Finish() + v := webcontext.Validator() + // Validation email address is unique in the database. uniq, err := UniqueEmail(ctx, dbConn, req.Email, "") if err != nil { return nil, err } - f := func(fl validator.FieldLevel) bool { - if fl.Field().String() == "invalid" { - return false - } - return uniq - } - - v := webcontext.Validator() - v.RegisterValidation("unique", f) + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq) // Validate the request. - err = v.Struct(req) + err = v.StructCtx(ctx, req) if err != nil { 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. if req.Email != nil { + // Validation email address is unique in the database. uniq, err := UniqueEmail(ctx, dbConn, *req.Email, req.ID) if err != nil { return err } - f := func(fl validator.FieldLevel) bool { - if fl.Field().String() == "invalid" { - return false - } - return uniq - } - v.RegisterValidation("unique", f) + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, uniq) + } else { + ctx = context.WithValue(ctx, webcontext.KeyTagUnique, true) } // Validate the request. - err := v.Struct(req) + err := v.StructCtx(ctx, req) if err != nil { return err } @@ -437,10 +427,10 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserUp var fields []string 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 { - fields = append(fields, query.Assign("name", req.LastName)) + fields = append(fields, query.Assign("last_name", req.LastName)) } if req.Email != nil { fields = append(fields, query.Assign("email", req.Email)) diff --git a/internal/user/user_test.go b/internal/user/user_test.go index 5ed46f0..6de345f 100644 --- a/internal/user/user_test.go +++ b/internal/user/user_test.go @@ -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" + "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.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", UserCreateRequest{ @@ -224,15 +225,16 @@ func TestCreateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { expectStr = tt.error.Error() } if errStr != expectStr { - t.Logf("\t\tGot : %+v", err) - t.Logf("\t\tWant: %+v", tt.error) + t.Logf("\t\tGot : %+v", errStr) + t.Logf("\t\tWant: %+v", expectStr) 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) } - 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.Fatalf("\t%s\tCreate failed.", tests.Failed) } @@ -422,15 +427,16 @@ func TestUpdateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { expectStr = tt.error.Error() } if errStr != expectStr { - t.Logf("\t\tGot : %+v", err) - t.Logf("\t\tWant: %+v", tt.error) + t.Logf("\t\tGot : %+v", errStr) + t.Logf("\t\tWant: %+v", expectStr) 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) } - 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.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. 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) if err == nil { t.Logf("\t\tWant: %+v", expectedErr) 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.Fatalf("\t%s\tValidation failed.", tests.Failed) } diff --git a/internal/user_account/user_account_test.go b/internal/user_account/user_account_test.go index fdbe986..d463a41 100644 --- a/internal/user_account/user_account_test.go +++ b/internal/user_account/user_account_test.go @@ -232,15 +232,16 @@ func TestCreateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { expectStr = tt.error.Error() } if errStr != expectStr { - t.Logf("\t\tGot : %+v", err) - t.Logf("\t\tWant: %+v", tt.error) + t.Logf("\t\tGot : %+v", errStr) + t.Logf("\t\tWant: %+v", expectStr) t.Fatalf("\t%s\tCreate user account failed.", tests.Failed) } } @@ -411,15 +412,16 @@ func TestUpdateValidation(t *testing.T) { // of type interface validator.ValidationErrorsTranslations var errStr string if err != nil { - errStr = err.Error() + errStr = strings.Replace(err.Error(), "{{", "", -1) + errStr = strings.Replace(errStr, "}}", "", -1) } var expectStr string if tt.error != nil { expectStr = tt.error.Error() } if errStr != expectStr { - t.Logf("\t\tGot : %+v", err) - t.Logf("\t\tWant: %+v", tt.error) + t.Logf("\t\tGot : %+v", errStr) + t.Logf("\t\tWant: %+v", expectStr) t.Fatalf("\t%s\tUpdate user account failed.", tests.Failed) } } diff --git a/tools/schema/main.go b/tools/schema/main.go index 20a43ca..bdf2588 100644 --- a/tools/schema/main.go +++ b/tools/schema/main.go @@ -126,7 +126,7 @@ func main() { // Start 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.Printf("main : Migrate : Completed")