1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-12-24 00:01:31 +02:00

checkpoint

This commit is contained in:
Lee Brown
2019-06-26 20:21:00 -08:00
parent b68bcf2c2c
commit 48ae19bd6a
23 changed files with 1952 additions and 859 deletions

View File

@@ -3,6 +3,7 @@ package account
import (
"context"
"database/sql"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"time"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
@@ -25,9 +26,6 @@ var (
// ErrNotFound abstracts the mgo not found error.
ErrNotFound = errors.New("Entity not found")
// ErrInvalidID occurs when an ID is not in a valid form.
ErrInvalidID = errors.New("ID is not in its proper form")
// ErrForbidden occurs when a user tries to do something that is forbidden to them according to our access control policies.
ErrForbidden = errors.New("Attempted action is not allowed")
)
@@ -243,6 +241,7 @@ func UniqueName(ctx context.Context, dbConn *sqlx.DB, name, accountId string) (b
var existingId string
err := dbConn.QueryRowContext(ctx, queryStr, args...).Scan(&existingId)
if err != nil && err != sql.ErrNoRows {
err = errors.Wrapf(err, "query - %s", query.String())
return false, err
@@ -261,8 +260,6 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
span, ctx := tracer.StartSpanFromContext(ctx, "internal.account.Create")
defer span.Finish()
v := validator.New()
// Validation email address is unique in the database.
uniq, err := UniqueName(ctx, dbConn, req.Name, "")
if err != nil {
@@ -274,6 +271,8 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
}
return uniq
}
v := web.NewValidator()
v.RegisterValidation("unique", f)
// Validate the request.
@@ -352,11 +351,11 @@ func Read(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, id string, i
query.Where(query.Equal("id", id))
res, err := find(ctx, claims, dbConn, query, []interface{}{}, includedArchived)
if err != nil {
return nil, err
} else if res == nil || len(res) == 0 {
if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "account %s not found", id)
return nil, err
} else if err != nil {
return nil, err
}
u := res[0]
@@ -368,7 +367,7 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
span, ctx := tracer.StartSpanFromContext(ctx, "internal.account.Update")
defer span.Finish()
v := validator.New()
v := web.NewValidator()
// Validation name is unique in the database.
if req.Name != nil {
@@ -380,6 +379,7 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accoun
if fl.Field().String() == "invalid" {
return false
}
return uniq
}
v.RegisterValidation("unique", f)
@@ -495,7 +495,8 @@ func Archive(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Accou
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -573,7 +574,8 @@ func Delete(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, accountID
}
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}

View File

@@ -29,25 +29,36 @@ func Authenticate(authenticator *auth.Authenticator) web.Middleware {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.mid.Authenticate")
defer span.Finish()
authHdr := r.Header.Get("Authorization")
if authHdr == "" {
err := errors.New("missing Authorization header")
return web.NewRequestError(err, http.StatusUnauthorized)
m := func() error {
authHdr := r.Header.Get("Authorization")
if authHdr == "" {
err := errors.New("missing Authorization header")
return web.NewRequestError(err, http.StatusUnauthorized)
}
tknStr, err := parseAuthHeader(authHdr)
if err != nil {
return web.NewRequestError(err, http.StatusUnauthorized)
}
claims, err := authenticator.ParseClaims(tknStr)
if err != nil {
return web.NewRequestError(err, http.StatusUnauthorized)
}
// Add claims to the context so they can be retrieved later.
ctx = context.WithValue(ctx, auth.Key, claims)
return nil
}
tknStr, err := parseAuthHeader(authHdr)
if err != nil {
return web.NewRequestError(err, http.StatusUnauthorized)
if err := m(); err != nil {
if web.RequestIsJson(r) {
return web.RespondJsonError(ctx, w, err)
}
return err
}
claims, err := authenticator.ParseClaims(tknStr)
if err != nil {
return web.NewRequestError(err, http.StatusUnauthorized)
}
// Add claims to the context so they can be retrieved later.
ctx = context.WithValue(ctx, auth.Key, claims)
return after(ctx, w, r, params)
}
@@ -68,14 +79,25 @@ func HasRole(roles ...string) web.Middleware {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.mid.HasRole")
defer span.Finish()
claims, ok := ctx.Value(auth.Key).(auth.Claims)
if !ok {
// TODO(jlw) should this be a web.Shutdown?
return errors.New("claims missing from context: HasRole called without/before Authenticate")
m := func() error {
claims, ok := ctx.Value(auth.Key).(auth.Claims)
if !ok {
// TODO(jlw) should this be a web.Shutdown?
return errors.New("claims missing from context: HasRole called without/before Authenticate")
}
if !claims.HasRole(roles...) {
return ErrForbidden
}
return nil
}
if !claims.HasRole(roles...) {
return ErrForbidden
if err := m(); err != nil {
if web.RequestIsJson(r) {
return web.RespondJsonError(ctx, w, err)
}
return err
}
return after(ctx, w, r, params)
@@ -97,3 +119,6 @@ func parseAuthHeader(bearerStr string) (string, error) {
return split[1], nil
}

View File

@@ -28,8 +28,14 @@ func Errors(log *log.Logger) web.Middleware {
log.Printf("%d : ERROR : %+v", span.Context().TraceID(), err)
// Respond to the error.
if err := web.RespondError(ctx, w, err); err != nil {
return err
if web.RequestIsJson(r) {
if err := web.RespondJsonError(ctx, w, err); err != nil {
return err
}
} else {
if err := web.RespondError(ctx, w, err); err != nil {
return err
}
}
// If we receive the shutdown err we need to return it

View File

@@ -2,6 +2,8 @@ package web
import (
"github.com/pkg/errors"
"gopkg.in/go-playground/validator.v9"
"net/http"
)
// FieldError is used to indicate an error with a specific request field.
@@ -27,6 +29,12 @@ type Error struct {
// NewRequestError wraps a provided error with an HTTP status code. This
// function should be used when handlers encounter expected errors.
func NewRequestError(err error, status int) error {
// if its a validation error then
if verr, ok := NewValidationError(err); ok {
return verr
}
return &Error{err, status, nil}
}
@@ -52,6 +60,35 @@ func NewShutdownError(message string) error {
return &shutdown{message}
}
// NewValidationError checks the error for validation errors and formats the correct response.
func NewValidationError(err error) (error, bool) {
// Use a type assertion to get the real error value.
verrors, ok := errors.Cause(err).(validator.ValidationErrors)
if !ok {
return err, false
}
// lang controls the language of the error messages. You could look at the
// Accept-Language header if you intend to support multiple languages.
lang, _ := translator.GetTranslator("en")
var fields []FieldError
for _, verror := range verrors {
field := FieldError{
Field: verror.Field(),
Error: verror.Translate(lang),
}
fields = append(fields, field)
}
return &Error{
Err: errors.New("field validation error"),
Status: http.StatusBadRequest,
Fields: fields,
}, true
}
// IsShutdown checks to see if the shutdown error is contained
// in the specified error value.
func IsShutdown(err error) bool {

View File

@@ -36,7 +36,21 @@ func init() {
en_translations.RegisterDefaultTranslations(validate, lang)
// Use JSON tag names for errors instead of Go struct names.
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
validate = NewValidator()
// Empty method that can be overwritten in business logic packages to prevent web.Decode from failing.
f := func(fl validator.FieldLevel) bool {
return true
}
validate.RegisterValidation("unique", f)
}
// NewValidator inits a new validator with custom settings.
func NewValidator() *validator.Validate {
var v = validator.New()
// Use JSON tag names for errors instead of Go struct names.
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
@@ -44,10 +58,7 @@ func init() {
return name
})
f := func(fl validator.FieldLevel) bool {
return true
}
validate.RegisterValidation("unique", f)
return v
}
// Decode reads the body of an HTTP request looking for a JSON document. The
@@ -56,45 +67,24 @@ func init() {
// If the provided value is a struct then it is checked for validation tags.
func Decode(r *http.Request, val interface{}) error {
if r.Method == http.MethodPost || r.Method == http.MethodPut || r.Method == http.MethodDelete {
if r.Method == http.MethodPost || r.Method == http.MethodPut || r.Method == http.MethodPatch || r.Method == http.MethodDelete {
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(val); err != nil {
err = errors.Wrap(err, "decode request body failed")
return NewRequestError(err, http.StatusBadRequest)
}
} else {
decoder := schema.NewDecoder()
if err := decoder.Decode(val, r.URL.Query()); err != nil {
err = errors.Wrap(err, "decode request query failed")
return NewRequestError(err, http.StatusBadRequest)
}
}
if err := validate.Struct(val); err != nil {
// Use a type assertion to get the real error value.
verrors, ok := err.(validator.ValidationErrors)
if !ok {
return err
}
// lang controls the language of the error messages. You could look at the
// Accept-Language header if you intend to support multiple languages.
lang, _ := translator.GetTranslator("en")
var fields []FieldError
for _, verror := range verrors {
field := FieldError{
Field: verror.Field(),
Error: verror.Translate(lang),
}
fields = append(fields, field)
}
return &Error{
Err: errors.New("field validation error"),
Status: http.StatusBadRequest,
Fields: fields,
}
verr, _ := NewValidationError(err)
return verr
}
return nil
@@ -139,3 +129,30 @@ func ExtractWhereArgs(where string) (string, []interface{}, error) {
return where, vals, nil
}
func RequestIsJson(r *http.Request) bool {
if r == nil {
return false
}
if v := r.Header.Get("Content-type"); v != "" {
for _, hv := range strings.Split(v, ";") {
if strings.ToLower(hv) == "application/json" {
return true
}
}
}
if v := r.URL.Query().Get("ResponseFormat"); v != "" {
if strings.ToLower(v) == "json" {
return true
}
}
if strings.HasSuffix(r.URL.Path, ".json") {
return true
}
return false
}

View File

@@ -32,7 +32,12 @@ func RespondJsonError(ctx context.Context, w http.ResponseWriter, err error) err
// If the error was of the type *Error, the handler has
// a specific status code and error to return.
if webErr, ok := errors.Cause(err).(*Error); ok {
webErr, ok := errors.Cause(err).(*Error)
if !ok {
webErr, ok = err.(*Error)
}
if ok {
er := ErrorResponse{
Error: webErr.Err.Error(),
Fields: webErr.Fields,

View File

@@ -4,12 +4,12 @@ import (
"context"
"database/sql"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"github.com/huandu/go-sqlbuilder"
"github.com/jmoiron/sqlx"
"github.com/pborman/uuid"
"github.com/pkg/errors"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/go-playground/validator.v9"
"time"
)
@@ -21,10 +21,9 @@ const (
var (
// ErrNotFound abstracts the postgres not found error.
ErrNotFound = errors.New("Entity not found")
// ErrForbidden occurs when a user tries to do something that is forbidden to them according to our access control policies.
ErrForbidden = errors.New("Attempted action is not allowed")
// ErrInvalidID occurs when an ID is not in a valid form.
ErrInvalidID = errors.New("ID is not in its proper form")
)
// projectMapColumns is the list of columns needed for mapRowsToProject
@@ -193,14 +192,16 @@ func find(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, query *sqlbu
func Read(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, id string, includedArchived bool) (*Project, error) {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Read")
defer span.Finish()
// Filter base select query by id
query := selectQuery()
query.Where(query.Equal("id", id))
res, err := find(ctx, claims, dbConn, query, []interface{}{}, includedArchived)
if err != nil {
if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "account %s not found", id)
return nil, err
} else if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "project %s not found", id)
} else if err != nil {
return nil, err
}
@@ -231,8 +232,8 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Projec
}
v := validator.New()
// Validate the request.
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return nil, err
@@ -301,8 +302,9 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Projec
func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req ProjectUpdateRequest, now time.Time) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Update")
defer span.Finish()
v := validator.New()
// Validate the request.
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
@@ -372,7 +374,8 @@ func Archive(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Proje
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -418,12 +421,15 @@ func Archive(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Proje
func Delete(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, id string) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Delete")
defer span.Finish()
// Defines the struct to apply validation
req := struct {
ID string `validate:"required,uuid"`
}{}
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package signup
import (
"context"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/account"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/user"
)
@@ -31,8 +32,33 @@ type SignupUser struct {
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password" example:"SecretString"`
}
// SignupResponse response signup with created account and user.
type SignupResponse struct {
// SignupResult response signup with created account and user.
type SignupResult struct {
Account *account.Account `json:"account"`
User *user.User `json:"user"`
}
// SignupResponse represents the user and account created for signup that is returned for display.
type SignupResponse struct {
Account *account.AccountResponse `json:"account"`
User *user.UserResponse `json:"user"`
}
// Response transforms SignupResult to SignupResponse that is used for display.
// Additional filtering by context values or translations could be applied.
func (m *SignupResult) Response(ctx context.Context) *SignupResponse {
if m == nil {
return nil
}
r := &SignupResponse{}
if m.Account != nil {
r.Account = m.Account.Response(ctx)
}
if m.User != nil {
r.User = m.User.Response(ctx)
}
return r
}

View File

@@ -2,6 +2,7 @@ package signup
import (
"context"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"time"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/account"
@@ -15,12 +16,10 @@ import (
// Signup performs the steps needed to create a new account, new user and then associate
// both records with a new user_account entry.
func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req SignupRequest, now time.Time) (*SignupResponse, error) {
func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req SignupRequest, now time.Time) (*SignupResult, error) {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.signup.Signup")
defer span.Finish()
v := validator.New()
// Validate the user email address is unique in the database.
uniqEmail, err := user.UniqueEmail(ctx, dbConn, req.User.Email, "")
if err != nil {
@@ -33,6 +32,7 @@ func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Signup
return nil, err
}
f := func(fl validator.FieldLevel) bool {
if fl.Field().String() == "invalid" {
return false
@@ -40,14 +40,16 @@ func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Signup
var uniq bool
switch fl.FieldName() {
case "Name":
case "Name", "name":
uniq = uniqName
case "Email":
case "Email", "email":
uniq = uniqEmail
}
return uniq
}
v := web.NewValidator()
v.RegisterValidation("unique", f)
// Validate the request.
@@ -56,7 +58,7 @@ func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Signup
return nil, err
}
var resp SignupResponse
var resp SignupResult
// UserCreateRequest contains information needed to create a new User.
userReq := user.UserCreateRequest{

View File

@@ -32,12 +32,12 @@ func TestSignupValidation(t *testing.T) {
var userTests = []struct {
name string
req SignupRequest
expected func(req SignupRequest, res *SignupResponse) *SignupResponse
expected func(req SignupRequest, res *SignupResult) *SignupResult
error error
}{
{"Required Fields",
SignupRequest{},
func(req SignupRequest, res *SignupResponse) *SignupResponse {
func(req SignupRequest, res *SignupResult) *SignupResult {
return nil
},
errors.New("Key: 'SignupRequest.Account.Name' Error:Field validation for 'Name' failed on the 'required' tag\n" +

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/rsa"
"database/sql"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"github.com/dgrijalva/jwt-go"
"strings"
"time"
@@ -15,7 +16,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"
)
// TokenGenerator is the behavior we need in our Authenticate to generate tokens for
@@ -80,7 +80,8 @@ func SwitchAccount(ctx context.Context, dbConn *sqlx.DB, tknGen TokenGenerator,
}
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return Token{}, err
}

View File

@@ -3,6 +3,7 @@ package user
import (
"context"
"database/sql"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"time"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
@@ -28,9 +29,6 @@ var (
// ErrNotFound abstracts the mgo not found error.
ErrNotFound = errors.New("Entity not found")
// ErrInvalidID occurs when an ID is not in a valid form.
ErrInvalidID = errors.New("ID is not in its proper form")
// ErrForbidden occurs when a user tries to do something that is forbidden to them according to our access control policies.
ErrForbidden = errors.New("Attempted action is not allowed")
@@ -261,8 +259,6 @@ 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 := validator.New()
// Validation email address is unique in the database.
uniq, err := UniqueEmail(ctx, dbConn, req.Email, "")
if err != nil {
@@ -274,6 +270,8 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserCr
}
return uniq
}
v := web.NewValidator()
v.RegisterValidation("unique", f)
// Validate the request.
@@ -356,11 +354,11 @@ func Read(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, id string, i
query.Where(query.Equal("id", id))
res, err := find(ctx, claims, dbConn, query, []interface{}{}, includedArchived)
if err != nil {
return nil, err
} else if res == nil || len(res) == 0 {
if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "user %s not found", id)
return nil, err
} else if err != nil {
return nil, err
}
u := res[0]
@@ -372,7 +370,7 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserUp
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user.Update")
defer span.Finish()
v := validator.New()
v := web.NewValidator()
// Validation email address is unique in the database.
if req.Email != nil {
@@ -458,7 +456,8 @@ func UpdatePassword(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, re
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -526,7 +525,8 @@ func Archive(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserA
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -604,7 +604,8 @@ func Delete(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, userID str
}
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/account"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"time"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/auth"
@@ -12,16 +13,12 @@ import (
"github.com/pborman/uuid"
"github.com/pkg/errors"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/go-playground/validator.v9"
)
var (
// ErrNotFound abstracts the mgo not found error.
ErrNotFound = errors.New("Entity not found")
// ErrInvalidID occurs when an ID is not in a valid form.
ErrInvalidID = errors.New("ID is not in its proper form")
// ErrForbidden occurs when a user tries to do something that is forbidden to them according to our access control policies.
ErrForbidden = errors.New("Attempted action is not allowed")
)
@@ -64,8 +61,6 @@ func mapAccountError(err error) error {
switch errors.Cause(err) {
case account.ErrNotFound:
err = ErrNotFound
case account.ErrInvalidID:
err = ErrInvalidID
case account.ErrForbidden:
err = ErrForbidden
}
@@ -206,7 +201,8 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserAc
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return nil, err
}
@@ -303,10 +299,10 @@ func Read(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, id string, i
query.Where(query.Equal("id", id))
res, err := find(ctx, claims, dbConn, query, []interface{}{}, includedArchived)
if err != nil {
if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "account %s not found", id)
return nil, err
} else if res == nil || len(res) == 0 {
err = errors.WithMessagef(ErrNotFound, "user account %s not found", id)
} else if err != nil {
return nil, err
}
u := res[0]
@@ -320,7 +316,8 @@ func Update(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserAc
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -392,7 +389,8 @@ func Archive(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserA
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}
@@ -443,7 +441,8 @@ func Delete(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserAc
defer span.Finish()
// Validate the request.
err := validator.New().Struct(req)
v := web.NewValidator()
err := v.Struct(req)
if err != nil {
return err
}