You've already forked golang-saas-starter-kit
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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" +
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user