feat(backend): add custom preferences
This commit is contained in:
parent
e8ea642260
commit
2c71741d7c
8 changed files with 39 additions and 34 deletions
|
@ -40,13 +40,13 @@ func (w *WordStatus) UnmarshalJSON(src []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WordStatus) Valid(extra ...WordStatus) bool {
|
func (w WordStatus) Valid(extra CustomPreferences) bool {
|
||||||
if w == StatusFavourite || w == StatusOkay || w == StatusJokingly || w == StatusFriendsOnly || w == StatusAvoid {
|
if w == StatusFavourite || w == StatusOkay || w == StatusJokingly || w == StatusFriendsOnly || w == StatusAvoid {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range extra {
|
for k := range extra {
|
||||||
if w == extra[i] {
|
if string(w) == k {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ type FieldEntry struct {
|
||||||
Status WordStatus `json:"status"`
|
Status WordStatus `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe FieldEntry) Validate() string {
|
func (fe FieldEntry) Validate(custom CustomPreferences) string {
|
||||||
if fe.Value == "" {
|
if fe.Value == "" {
|
||||||
return "value cannot be empty"
|
return "value cannot be empty"
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func (fe FieldEntry) Validate() string {
|
||||||
return fmt.Sprintf("name must be %d characters or less, is %d", FieldEntryMaxLength, len([]rune(fe.Value)))
|
return fmt.Sprintf("name must be %d characters or less, is %d", FieldEntryMaxLength, len([]rune(fe.Value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fe.Status.Valid() {
|
if !fe.Status.Valid(custom) {
|
||||||
return "status is invalid"
|
return "status is invalid"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ type PronounEntry struct {
|
||||||
Status WordStatus `json:"status"`
|
Status WordStatus `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PronounEntry) Validate() string {
|
func (p PronounEntry) Validate(custom CustomPreferences) string {
|
||||||
if p.Pronouns == "" {
|
if p.Pronouns == "" {
|
||||||
return "pronouns cannot be empty"
|
return "pronouns cannot be empty"
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func (p PronounEntry) Validate() string {
|
||||||
return fmt.Sprintf("pronouns must be %d characters or less, is %d", FieldEntryMaxLength, len([]rune(p.Pronouns)))
|
return fmt.Sprintf("pronouns must be %d characters or less, is %d", FieldEntryMaxLength, len([]rune(p.Pronouns)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.Status.Valid() {
|
if !p.Status.Valid(custom) {
|
||||||
return "status is invalid"
|
return "status is invalid"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Field struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this field. If it is invalid, a non-empty string is returned as error message.
|
// Validate validates this field. If it is invalid, a non-empty string is returned as error message.
|
||||||
func (f Field) Validate() string {
|
func (f Field) Validate(custom CustomPreferences) string {
|
||||||
if f.Name == "" {
|
if f.Name == "" {
|
||||||
return "name cannot be empty"
|
return "name cannot be empty"
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func (f Field) Validate() string {
|
||||||
return fmt.Sprintf("entries.%d: max length is %d characters, length is %d", i, FieldEntryMaxLength, length)
|
return fmt.Sprintf("entries.%d: max length is %d characters, length is %d", i, FieldEntryMaxLength, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !entry.Status.Valid() {
|
if !entry.Status.Valid(custom) {
|
||||||
return fmt.Sprintf("entries.%d: status is invalid", i)
|
return fmt.Sprintf("entries.%d: status is invalid", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,8 +498,9 @@ func (db *DB) UpdateUser(
|
||||||
memberTitle *string, listPrivate *bool,
|
memberTitle *string, listPrivate *bool,
|
||||||
links *[]string,
|
links *[]string,
|
||||||
avatar *string,
|
avatar *string,
|
||||||
|
customPreferences *CustomPreferences,
|
||||||
) (u User, err error) {
|
) (u User, err error) {
|
||||||
if displayName == nil && bio == nil && links == nil && avatar == nil && memberTitle == nil && listPrivate == nil {
|
if displayName == nil && bio == nil && links == nil && avatar == nil && memberTitle == nil && listPrivate == nil && customPreferences == nil {
|
||||||
sql, args, err := sq.Select("*").From("users").Where("id = ?", id).ToSql()
|
sql, args, err := sq.Select("*").From("users").Where("id = ?", id).ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u, errors.Wrap(err, "building sql")
|
return u, errors.Wrap(err, "building sql")
|
||||||
|
@ -541,6 +542,9 @@ func (db *DB) UpdateUser(
|
||||||
if listPrivate != nil {
|
if listPrivate != nil {
|
||||||
builder = builder.Set("list_private", *listPrivate)
|
builder = builder.Set("list_private", *listPrivate)
|
||||||
}
|
}
|
||||||
|
if customPreferences != nil {
|
||||||
|
builder = builder.Set("custom_preferences", *customPreferences)
|
||||||
|
}
|
||||||
|
|
||||||
if avatar != nil {
|
if avatar != nil {
|
||||||
if *avatar == "" {
|
if *avatar == "" {
|
||||||
|
|
|
@ -103,15 +103,15 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("name", &cmr.Names); err != nil {
|
if err := validateSlicePtr("name", &cmr.Names, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("pronoun", &cmr.Pronouns); err != nil {
|
if err := validateSlicePtr("pronoun", &cmr.Pronouns, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("field", &cmr.Fields); err != nil {
|
if err := validateSlicePtr("field", &cmr.Fields, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +186,12 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type validator interface {
|
type validator interface {
|
||||||
Validate() string
|
Validate(custom db.CustomPreferences) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateSlicePtr validates a slice of validators.
|
// validateSlicePtr validates a slice of validators.
|
||||||
// If the slice is nil, a nil error is returned (assuming that the field is not required)
|
// If the slice is nil, a nil error is returned (assuming that the field is not required)
|
||||||
func validateSlicePtr[T validator](typ string, slice *[]T) *server.APIError {
|
func validateSlicePtr[T validator](typ string, slice *[]T, custom db.CustomPreferences) *server.APIError {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ func validateSlicePtr[T validator](typ string, slice *[]T) *server.APIError {
|
||||||
|
|
||||||
// validate all fields
|
// validate all fields
|
||||||
for i, pronouns := range *slice {
|
for i, pronouns := range *slice {
|
||||||
if s := pronouns.Validate(); s != "" {
|
if s := pronouns.Validate(custom); s != "" {
|
||||||
return &server.APIError{
|
return &server.APIError{
|
||||||
Code: server.ErrBadRequest,
|
Code: server.ErrBadRequest,
|
||||||
Details: fmt.Sprintf("%s %d: %s", typ, i+1, s),
|
Details: fmt.Sprintf("%s %d: %s", typ, i+1, s),
|
||||||
|
|
|
@ -41,6 +41,11 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
return server.APIError{Code: server.ErrMemberNotFound}
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u, err := s.DB.User(ctx, claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "getting user")
|
||||||
|
}
|
||||||
|
|
||||||
m, err := s.DB.Member(ctx, id)
|
m, err := s.DB.Member(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == db.ErrMemberNotFound {
|
if err == db.ErrMemberNotFound {
|
||||||
|
@ -148,15 +153,15 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("name", req.Names); err != nil {
|
if err := validateSlicePtr("name", req.Names, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("pronoun", req.Pronouns); err != nil {
|
if err := validateSlicePtr("pronoun", req.Pronouns, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("field", req.Fields); err != nil {
|
if err := validateSlicePtr("field", req.Fields, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,11 +276,6 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := s.DB.User(ctx, claims.UserID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "getting user")
|
|
||||||
}
|
|
||||||
|
|
||||||
// echo the updated member back on success
|
// echo the updated member back on success
|
||||||
render.JSON(w, r, dbMemberToMember(u, m, fields, true))
|
render.JSON(w, r, dbMemberToMember(u, m, fields, true))
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -59,7 +59,8 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error {
|
||||||
req.Fields == nil &&
|
req.Fields == nil &&
|
||||||
req.Names == nil &&
|
req.Names == nil &&
|
||||||
req.Pronouns == nil &&
|
req.Pronouns == nil &&
|
||||||
req.Avatar == nil {
|
req.Avatar == nil &&
|
||||||
|
req.CustomPreferences == nil {
|
||||||
return server.APIError{
|
return server.APIError{
|
||||||
Code: server.ErrBadRequest,
|
Code: server.ErrBadRequest,
|
||||||
Details: "Data must not be empty",
|
Details: "Data must not be empty",
|
||||||
|
@ -105,15 +106,15 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("name", req.Names); err != nil {
|
if err := validateSlicePtr("name", req.Names, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("pronoun", req.Pronouns); err != nil {
|
if err := validateSlicePtr("pronoun", req.Pronouns, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateSlicePtr("field", req.Fields); err != nil {
|
if err := validateSlicePtr("field", req.Fields, u.CustomPreferences); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +202,7 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err = s.DB.UpdateUser(ctx, tx, claims.UserID, req.DisplayName, req.Bio, req.MemberTitle, req.ListPrivate, req.Links, avatarHash)
|
u, err = s.DB.UpdateUser(ctx, tx, claims.UserID, req.DisplayName, req.Bio, req.MemberTitle, req.ListPrivate, req.Links, avatarHash, req.CustomPreferences)
|
||||||
if err != nil && errors.Cause(err) != db.ErrNothingToUpdate {
|
if err != nil && errors.Cause(err) != db.ErrNothingToUpdate {
|
||||||
log.Errorf("updating user: %v", err)
|
log.Errorf("updating user: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -278,12 +279,12 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type validator interface {
|
type validator interface {
|
||||||
Validate() string
|
Validate(custom db.CustomPreferences) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateSlicePtr validates a slice of validators.
|
// validateSlicePtr validates a slice of validators.
|
||||||
// If the slice is nil, a nil error is returned (assuming that the field is not required)
|
// If the slice is nil, a nil error is returned (assuming that the field is not required)
|
||||||
func validateSlicePtr[T validator](typ string, slice *[]T) *server.APIError {
|
func validateSlicePtr[T validator](typ string, slice *[]T, custom db.CustomPreferences) *server.APIError {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -303,7 +304,7 @@ func validateSlicePtr[T validator](typ string, slice *[]T) *server.APIError {
|
||||||
|
|
||||||
// validate all fields
|
// validate all fields
|
||||||
for i, pronouns := range *slice {
|
for i, pronouns := range *slice {
|
||||||
if s := pronouns.Validate(); s != "" {
|
if s := pronouns.Validate(custom); s != "" {
|
||||||
return &server.APIError{
|
return &server.APIError{
|
||||||
Code: server.ErrBadRequest,
|
Code: server.ErrBadRequest,
|
||||||
Details: fmt.Sprintf("%s %d: %s", typ, i+1, s),
|
Details: fmt.Sprintf("%s %d: %s", typ, i+1, s),
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -15,6 +15,7 @@ require (
|
||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/jackc/pgx/v5 v5.3.1
|
github.com/jackc/pgx/v5 v5.3.1
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mediocregopher/radix/v4 v4.1.2
|
github.com/mediocregopher/radix/v4 v4.1.2
|
||||||
|
@ -40,7 +41,6 @@ require (
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/s2a-go v0.1.0 // indirect
|
github.com/google/s2a-go v0.1.0 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
|
|
@ -48,7 +48,7 @@ func run(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = pg.UpdateUser(ctx, tx, u.ID, ptr("testing"), ptr("This is a bio!"), nil, ptr(false), &[]string{"https://pronouns.cc"}, nil)
|
_, err = pg.UpdateUser(ctx, tx, u.ID, ptr("testing"), ptr("This is a bio!"), nil, ptr(false), &[]string{"https://pronouns.cc"}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("error setting user info:", err)
|
fmt.Println("error setting user info:", err)
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue