diff --git a/api/system/spec.json b/api/system/spec.json index 9a50e7635..da5b2fb04 100644 --- a/api/system/spec.json +++ b/api/system/spec.json @@ -403,6 +403,13 @@ "Client ID", "Session ID" ], + "struct": [ + { + "imports": [ + "github.com/crusttech/crust/system/types" + ] + } + ], "apis": [ { "name": "list", @@ -459,7 +466,7 @@ }, { "name": "kind", - "type": "string", + "type": "types.UserKind", "required": false, "title": "Kind (normal, bot)" } @@ -501,7 +508,7 @@ }, { "name": "kind", - "type": "string", + "type": "types.UserKind", "required": false, "title": "Kind (normal, bot)" } diff --git a/api/system/spec/user.json b/api/system/spec/user.json index 18d13ab65..82e1d5cc6 100644 --- a/api/system/spec/user.json +++ b/api/system/spec/user.json @@ -1,7 +1,13 @@ { "Title": "Users", "Interface": "User", - "Struct": null, + "Struct": [ + { + "imports": [ + "github.com/crusttech/crust/system/types" + ] + } + ], "Parameters": null, "Protocol": "", "Authentication": [ @@ -67,7 +73,7 @@ "name": "kind", "required": false, "title": "Kind (normal, bot)", - "type": "string" + "type": "types.UserKind" } ] } @@ -109,7 +115,7 @@ "name": "kind", "required": false, "title": "Kind (normal, bot)", - "type": "string" + "type": "types.UserKind" } ] } diff --git a/docs/system/README.md b/docs/system/README.md index c8cdc347e..d276cf6a2 100644 --- a/docs/system/README.md +++ b/docs/system/README.md @@ -558,7 +558,7 @@ An organisation may have many roles. Roles may have many channels available. Acc | email | string | POST | Email | N/A | YES | | name | string | POST | Name | N/A | NO | | handle | string | POST | Handle | N/A | NO | -| kind | string | POST | Kind (normal, bot) | N/A | NO | +| kind | types.UserKind | POST | Kind (normal, bot) | N/A | NO | ## Update user details @@ -576,7 +576,7 @@ An organisation may have many roles. Roles may have many channels available. Acc | email | string | POST | Email | N/A | YES | | name | string | POST | Name | N/A | YES | | handle | string | POST | Handle | N/A | NO | -| kind | string | POST | Kind (normal, bot) | N/A | NO | +| kind | types.UserKind | POST | Kind (normal, bot) | N/A | NO | ## Read user details and memberships diff --git a/system/rest/request/user.go b/system/rest/request/user.go index 2d1e09d17..7b2597ea6 100644 --- a/system/rest/request/user.go +++ b/system/rest/request/user.go @@ -25,6 +25,8 @@ import ( "github.com/go-chi/chi" "github.com/pkg/errors" + + "github.com/crusttech/crust/system/types" ) var _ = chi.URLParam @@ -91,7 +93,7 @@ type UserCreate struct { Email string Name string Handle string - Kind string + Kind types.UserKind } func NewUserCreate() *UserCreate { @@ -139,7 +141,7 @@ func (usReq *UserCreate) Fill(r *http.Request) (err error) { } if val, ok := post["kind"]; ok { - usReq.Kind = val + usReq.Kind = types.UserKind(val) } return err @@ -153,7 +155,7 @@ type UserUpdate struct { Email string Name string Handle string - Kind string + Kind types.UserKind } func NewUserUpdate() *UserUpdate { @@ -202,7 +204,7 @@ func (usReq *UserUpdate) Fill(r *http.Request) (err error) { } if val, ok := post["kind"]; ok { - usReq.Kind = val + usReq.Kind = types.UserKind(val) } return err diff --git a/system/types/user.go b/system/types/user.go index 6f1d84321..eeed22856 100644 --- a/system/types/user.go +++ b/system/types/user.go @@ -1,9 +1,11 @@ package types import ( + "database/sql/driver" + "encoding/json" "time" - "github.com/jmoiron/sqlx/types" + "github.com/pkg/errors" "golang.org/x/crypto/bcrypt" "github.com/crusttech/crust/internal/rules" @@ -11,14 +13,15 @@ import ( type ( User struct { - ID uint64 `json:"userID,string" db:"id"` - Username string `json:"username" db:"username"` - Email string `json:"email" db:"email"` - Name string `json:"name" db:"name"` - Handle string `json:"handle" db:"handle"` - Kind string `json:"kind" db:"kind"` - SatosaID string `json:"-" db:"satosa_id"` - Meta types.JSONText `json:"-" db:"meta"` + ID uint64 `json:"userID,string" db:"id"` + Username string `json:"username" db:"username"` + Email string `json:"email" db:"email"` + Name string `json:"name" db:"name"` + Handle string `json:"handle" db:"handle"` + Kind UserKind `json:"kind" db:"kind"` + SatosaID string `json:"-" db:"satosa_id"` + + Meta *UserMeta `json:"meta" db:"meta"` OrganisationID uint64 `json:"organisationID,string" db:"rel_organisation"` RelatedUserID uint64 `json:"relatedUserID,string" db:"rel_user_id"` @@ -34,12 +37,23 @@ type ( Roles []*Role `json:"roles,omitempty" db:"-"` } + UserMeta struct { + Avatar string `json:"avatar,omitempty"` + } + UserFilter struct { Query string Email string Username string OrderBy string } + + UserKind string +) + +const ( + NormalUser UserKind = "" + BotUser = "bot" ) func (u *User) Valid() bool { @@ -68,3 +82,21 @@ func (u *User) GeneratePassword(password string) error { func (u *User) PermissionResource() rules.Resource { return UserPermissionResource.AppendID(u.ID) } + +func (meta *UserMeta) Scan(value interface{}) error { + switch value.(type) { + case nil: + *meta = UserMeta{} + return nil + case []uint8: + if err := json.Unmarshal(value.([]byte), meta); err != nil { + return errors.Wrapf(err, "Can not scan '%v' into User.Meta", value) + } + return nil + } + return errors.Errorf("User.Meta: unknown type %T, expected []uint8", value) +} + +func (meta *UserMeta) Value() (driver.Value, error) { + return json.Marshal(meta) +}