Compare commits

...

3 Commits

Author SHA1 Message Date
Karan Balani
f40c178317 fix: import in migrations 2026-03-17 15:27:20 +05:30
Karan Balani
7a6cfa356e chore: add updatable user model for open api spec with required tags 2026-03-17 15:27:20 +05:30
Karan Balani
3b3190ff38 refactor: separate db and domain models for user - consistent with other modules 2026-03-17 15:27:19 +05:30
13 changed files with 195 additions and 108 deletions

View File

@@ -186,7 +186,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint lists all users",
Request: nil,
RequestContentType: "",
Response: make([]*usertypes.GettableUser, 0),
Response: make([]*usertypes.User, 0),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
@@ -203,7 +203,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint returns the user I belong to",
Request: nil,
RequestContentType: "",
Response: new(usertypes.GettableUser),
Response: new(usertypes.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
@@ -220,7 +220,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint returns the user by id",
Request: nil,
RequestContentType: "",
Response: new(usertypes.GettableUser),
Response: new(usertypes.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusNotFound},
@@ -235,9 +235,9 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Tags: []string{"users"},
Summary: "Update user",
Description: "This endpoint updates the user by id",
Request: new(usertypes.User),
Request: new(usertypes.UpdatableUser),
RequestContentType: "application/json",
Response: new(usertypes.GettableUser),
Response: new(usertypes.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},

View File

@@ -31,7 +31,7 @@ func (module *module) Create(ctx context.Context, timestamp int64, name string,
funnel.CreatedBy = userID.String()
// Set up the user relationship
funnel.CreatedByUser = &usertypes.User{
funnel.CreatedByUser = &usertypes.StorableUser{
Identifiable: types.Identifiable{
ID: userID,
},

View File

@@ -21,11 +21,15 @@ func NewGetter(store usertypes.UserStore, flagger flagger.Flagger) user.Getter {
}
func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*usertypes.User, error) {
return module.store.GetRootUserByOrgID(ctx, orgID)
storable, err := module.store.GetRootUserByOrgID(ctx, orgID)
if err != nil {
return nil, err
}
return usertypes.NewUserFromStorable(storable), nil
}
func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*usertypes.User, error) {
users, err := module.store.ListUsersByOrgID(ctx, orgID)
storableUsers, err := module.store.ListUsersByOrgID(ctx, orgID)
if err != nil {
return nil, err
}
@@ -35,46 +39,46 @@ func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*us
hideRootUsers := module.flagger.BooleanOrEmpty(ctx, flagger.FeatureHideRootUser, evalCtx)
if hideRootUsers {
users = slices.DeleteFunc(users, func(user *usertypes.User) bool { return user.IsRoot })
storableUsers = slices.DeleteFunc(storableUsers, func(user *usertypes.StorableUser) bool { return user.IsRoot })
}
return users, nil
return usertypes.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*usertypes.User, error) {
users, err := module.store.GetUsersByEmail(ctx, email)
storableUsers, err := module.store.GetUsersByEmail(ctx, email)
if err != nil {
return nil, err
}
return users, nil
return usertypes.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*usertypes.User, error) {
user, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
storableUser, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
if err != nil {
return nil, err
}
return user, nil
return usertypes.NewUserFromStorable(storableUser), nil
}
func (module *getter) Get(ctx context.Context, id valuer.UUID) (*usertypes.User, error) {
user, err := module.store.GetUser(ctx, id)
storableUser, err := module.store.GetUser(ctx, id)
if err != nil {
return nil, err
}
return user, nil
return usertypes.NewUserFromStorable(storableUser), nil
}
func (module *getter) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*usertypes.User, error) {
users, err := module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
storableUsers, err := module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
if err != nil {
return nil, err
}
return users, nil
return usertypes.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {
@@ -105,10 +109,10 @@ func (module *getter) GetFactorPasswordByUserID(ctx context.Context, userID valu
}
func (module *getter) GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*usertypes.User, *usertypes.FactorPassword, error) {
user, factorPassword, err := module.store.GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx, email, orgID)
storableUser, factorPassword, err := module.store.GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx, email, orgID)
if err != nil {
return nil, nil, err
}
return user, factorPassword, nil
return usertypes.NewUserFromStorable(storableUser), factorPassword, nil
}

View File

@@ -52,7 +52,7 @@ func NewModule(store usertypes.UserStore, tokenizer tokenizer.Tokenizer, emailin
func (m *Module) AcceptInvite(ctx context.Context, token string, password string) (*usertypes.User, error) {
// get the user by reset password token
user, err := m.store.GetUserByResetPasswordToken(ctx, token)
storableUser, err := m.store.GetUserByResetPasswordToken(ctx, token)
if err != nil {
return nil, err
}
@@ -64,21 +64,23 @@ func (m *Module) AcceptInvite(ctx context.Context, token string, password string
}
// query the user again
user, err = m.store.GetByOrgIDAndID(ctx, user.OrgID, user.ID)
storableUser, err = m.store.GetByOrgIDAndID(ctx, valuer.MustNewUUID(storableUser.OrgID), storableUser.ID)
if err != nil {
return nil, err
}
return user, nil
return usertypes.NewUserFromStorable(storableUser), nil
}
func (m *Module) GetInviteByToken(ctx context.Context, token string) (*usertypes.Invite, error) {
// get the user
user, err := m.store.GetUserByResetPasswordToken(ctx, token)
storableUser, err := m.store.GetUserByResetPasswordToken(ctx, token)
if err != nil {
return nil, err
}
user := usertypes.NewUserFromStorable(storableUser)
// create a dummy invite obj for backward compatibility
invite := &usertypes.Invite{
Identifiable: types.Identifiable{
@@ -110,11 +112,13 @@ func (m *Module) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, userID
for idx, invite := range bulkInvites.Invites {
emails[idx] = invite.Email.StringValue()
}
users, err := m.store.GetUsersByEmailsOrgIDAndStatuses(ctx, orgID, emails, []string{usertypes.UserStatusActive.StringValue(), usertypes.UserStatusPendingInvite.StringValue()})
storableUsers, err := m.store.GetUsersByEmailsOrgIDAndStatuses(ctx, orgID, emails, []string{usertypes.UserStatusActive.StringValue(), usertypes.UserStatusPendingInvite.StringValue()})
if err != nil {
return nil, err
}
users := usertypes.NewUsersFromStorables(storableUsers)
if len(users) > 0 {
if err := users[0].ErrIfRoot(); err != nil {
return nil, errors.WithAdditionalf(err, "Cannot send invite to root user")
@@ -221,12 +225,13 @@ func (m *Module) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, userID
func (m *Module) ListInvite(ctx context.Context, orgID string) ([]*usertypes.Invite, error) {
// find all the users with pending_invite status
users, err := m.store.ListUsersByOrgID(ctx, valuer.MustNewUUID(orgID))
storableUsers, err := m.store.ListUsersByOrgID(ctx, valuer.MustNewUUID(orgID))
if err != nil {
return nil, err
}
pendingUsers := slices.DeleteFunc(users, func(user *usertypes.User) bool { return user.Status != usertypes.UserStatusPendingInvite })
pendingStorableUsers := slices.DeleteFunc(storableUsers, func(user *usertypes.StorableUser) bool { return user.Status != usertypes.UserStatusPendingInvite })
pendingUsers := usertypes.NewUsersFromStorables(pendingStorableUsers)
var invites []*usertypes.Invite
@@ -269,7 +274,7 @@ func (module *Module) CreateUser(ctx context.Context, input *usertypes.User, opt
}
if err := module.store.RunInTx(ctx, func(ctx context.Context) error {
if err := module.store.CreateUser(ctx, input); err != nil {
if err := module.store.CreateUser(ctx, usertypes.NewStorableUser(input)); err != nil {
return err
}
@@ -292,11 +297,13 @@ func (module *Module) CreateUser(ctx context.Context, input *usertypes.User, opt
}
func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, user *usertypes.User, updatedBy string) (*usertypes.User, error) {
existingUser, err := m.store.GetUser(ctx, valuer.MustNewUUID(id))
existingStorableUser, err := m.store.GetUser(ctx, valuer.MustNewUUID(id))
if err != nil {
return nil, err
}
existingUser := usertypes.NewUserFromStorable(existingStorableUser)
if err := existingUser.ErrIfRoot(); err != nil {
return nil, errors.WithAdditionalf(err, "cannot update root user")
}
@@ -351,7 +358,8 @@ func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, u
}
func (module *Module) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *usertypes.User) error {
if err := module.store.UpdateUser(ctx, orgID, user); err != nil {
storableUser := usertypes.NewStorableUser(user)
if err := module.store.UpdateUser(ctx, orgID, storableUser); err != nil {
return err
}
@@ -367,11 +375,13 @@ func (module *Module) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user
}
func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error {
user, err := module.store.GetUser(ctx, valuer.MustNewUUID(id))
storableUser, err := module.store.GetUser(ctx, valuer.MustNewUUID(id))
if err != nil {
return err
}
user := usertypes.NewUserFromStorable(storableUser)
if err := user.ErrIfRoot(); err != nil {
return errors.WithAdditionalf(err, "cannot delete root user")
}
@@ -413,11 +423,13 @@ func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id stri
}
func (module *Module) GetOrCreateResetPasswordToken(ctx context.Context, userID valuer.UUID) (*usertypes.ResetPasswordToken, error) {
user, err := module.store.GetUser(ctx, userID)
storableUser, err := module.store.GetUser(ctx, userID)
if err != nil {
return nil, err
}
user := usertypes.NewUserFromStorable(storableUser)
if err := user.ErrIfRoot(); err != nil {
return nil, errors.WithAdditionalf(err, "cannot reset password for root user")
}
@@ -535,11 +547,13 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
return err
}
user, err := module.store.GetUser(ctx, valuer.MustNewUUID(password.UserID))
storableUser, err := module.store.GetUser(ctx, valuer.MustNewUUID(password.UserID))
if err != nil {
return err
}
user := usertypes.NewUserFromStorable(storableUser)
// handle deleted user
if err := user.ErrIfDeleted(); err != nil {
return errors.WithAdditionalf(err, "deleted users cannot reset their password")
@@ -570,7 +584,7 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
if err := user.UpdateStatus(usertypes.UserStatusActive); err != nil {
return err
}
if err := module.store.UpdateUser(ctx, user.OrgID, user); err != nil {
if err := module.store.UpdateUser(ctx, user.OrgID, usertypes.NewStorableUser(user)); err != nil {
return err
}
}
@@ -588,11 +602,13 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
}
func (module *Module) UpdatePassword(ctx context.Context, userID valuer.UUID, oldpasswd string, passwd string) error {
user, err := module.store.GetUser(ctx, userID)
storableUser, err := module.store.GetUser(ctx, userID)
if err != nil {
return err
}
user := usertypes.NewUserFromStorable(storableUser)
if err := user.ErrIfDeleted(); err != nil {
return errors.WithAdditionalf(err, "cannot change password for deleted user")
}
@@ -744,11 +760,13 @@ func (module *Module) Collect(ctx context.Context, orgID valuer.UUID) (map[strin
// this function restricts that only one non-deleted user email can exist for an org ID, if found more, it throws an error
func (module *Module) GetNonDeletedUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*usertypes.User, error) {
existingUsers, err := module.store.GetUsersByEmailAndOrgID(ctx, email, orgID)
existingStorableUsers, err := module.store.GetUsersByEmailAndOrgID(ctx, email, orgID)
if err != nil {
return nil, err
}
existingUsers := usertypes.NewUsersFromStorables(existingStorableUsers)
// filter out the deleted users
existingUsers = slices.DeleteFunc(existingUsers, func(user *usertypes.User) bool { return user.ErrIfDeleted() != nil })
@@ -767,7 +785,7 @@ func (module *Module) GetNonDeletedUserByEmailAndOrgID(ctx context.Context, emai
func (module *Module) createUserWithoutGrant(ctx context.Context, input *usertypes.User, opts ...root.CreateUserOption) error {
createUserOpts := root.NewCreateUserOptions(opts...)
if err := module.store.RunInTx(ctx, func(ctx context.Context) error {
if err := module.store.CreateUser(ctx, input); err != nil {
if err := module.store.CreateUser(ctx, usertypes.NewStorableUser(input)); err != nil {
return err
}
@@ -803,7 +821,7 @@ func (module *Module) activatePendingUser(ctx context.Context, user *usertypes.U
if err := user.UpdateStatus(usertypes.UserStatusActive); err != nil {
return err
}
err = module.store.UpdateUser(ctx, user.OrgID, user)
err = module.store.UpdateUser(ctx, user.OrgID, usertypes.NewStorableUser(user))
if err != nil {
return err
}

View File

@@ -130,10 +130,11 @@ func (s *service) reconcileByName(ctx context.Context) error {
}
func (s *service) reconcileRootUser(ctx context.Context, orgID valuer.UUID) error {
existingRoot, err := s.store.GetRootUserByOrgID(ctx, orgID)
existingStorableRoot, err := s.store.GetRootUserByOrgID(ctx, orgID)
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
return err
}
existingRoot := usertypes.NewUserFromStorable(existingStorableRoot)
if existingRoot == nil {
return s.createOrPromoteRootUser(ctx, orgID)

View File

@@ -39,7 +39,7 @@ func (store *store) CreatePassword(ctx context.Context, password *usertypes.Fact
return nil
}
func (store *store) CreateUser(ctx context.Context, user *usertypes.User) error {
func (store *store) CreateUser(ctx context.Context, user *usertypes.StorableUser) error {
_, err := store.
sqlstore.
BunDBCtx(ctx).
@@ -52,8 +52,8 @@ func (store *store) CreateUser(ctx context.Context, user *usertypes.User) error
return nil
}
func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*usertypes.User, error) {
var users []*usertypes.User
func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*usertypes.StorableUser, error) {
var users []*usertypes.StorableUser
err := store.
sqlstore.
@@ -69,8 +69,8 @@ func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]
return users, nil
}
func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*usertypes.User, error) {
user := new(usertypes.User)
func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*usertypes.StorableUser, error) {
user := new(usertypes.StorableUser)
err := store.
sqlstore.
@@ -86,8 +86,8 @@ func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*usertypes.Use
return user, nil
}
func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*usertypes.User, error) {
user := new(usertypes.User)
func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*usertypes.StorableUser, error) {
user := new(usertypes.StorableUser)
err := store.
sqlstore.
@@ -104,8 +104,8 @@ func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id v
return user, nil
}
func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*usertypes.User, error) {
var users []*usertypes.User
func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*usertypes.StorableUser, error) {
var users []*usertypes.StorableUser
err := store.
sqlstore.
@@ -122,8 +122,8 @@ func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Em
return users, nil
}
func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role authtypes.LegacyRole, orgID valuer.UUID) ([]*usertypes.User, error) {
var users []*usertypes.User
func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role authtypes.LegacyRole, orgID valuer.UUID) ([]*usertypes.StorableUser, error) {
var users []*usertypes.StorableUser
err := store.
sqlstore.
@@ -141,7 +141,7 @@ func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role autht
return users, nil
}
func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *usertypes.User) error {
func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *usertypes.StorableUser) error {
_, err := store.
sqlstore.
BunDBCtx(ctx).
@@ -162,8 +162,8 @@ func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *use
return nil
}
func (store *store) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*usertypes.GettableUser, error) {
users := []*usertypes.User{}
func (store *store) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*usertypes.StorableUser, error) {
users := []*usertypes.StorableUser{}
err := store.
sqlstore.
@@ -247,7 +247,7 @@ func (store *store) DeleteUser(ctx context.Context, orgID string, id string) err
// delete user
_, err = tx.NewDelete().
Model(new(usertypes.User)).
Model(new(usertypes.StorableUser)).
Where("org_id = ?", orgID).
Where("id = ?", id).
Exec(ctx)
@@ -332,7 +332,7 @@ func (store *store) SoftDeleteUser(ctx context.Context, orgID string, id string)
// soft delete user
now := time.Now()
_, err = tx.NewUpdate().
Model(new(usertypes.User)).
Model(new(usertypes.StorableUser)).
Set("status = ?", usertypes.UserStatusDeleted).
Set("deleted_at = ?", now).
Set("updated_at = ?", now).
@@ -563,7 +563,7 @@ func (store *store) GetAPIKey(ctx context.Context, orgID, id valuer.UUID) (*user
}
func (store *store) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {
user := new(usertypes.User)
user := new(usertypes.StorableUser)
count, err := store.
sqlstore.
@@ -580,7 +580,7 @@ func (store *store) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64,
}
func (store *store) CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error) {
user := new(usertypes.User)
user := new(usertypes.StorableUser)
var results []struct {
Status valuer.String `bun:"status"`
Count int64 `bun:"count"`
@@ -633,8 +633,8 @@ func (store *store) RunInTx(ctx context.Context, cb func(ctx context.Context) er
})
}
func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*usertypes.User, error) {
user := new(usertypes.User)
func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*usertypes.StorableUser, error) {
user := new(usertypes.StorableUser)
err := store.
sqlstore.
BunDBCtx(ctx).
@@ -649,8 +649,8 @@ func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (
return user, nil
}
func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*usertypes.User, error) {
users := []*usertypes.User{}
func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*usertypes.StorableUser, error) {
users := []*usertypes.StorableUser{}
err := store.
sqlstore.
BunDB().
@@ -666,8 +666,8 @@ func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.
return users, nil
}
func (store *store) GetUserByResetPasswordToken(ctx context.Context, token string) (*usertypes.User, error) {
user := new(usertypes.User)
func (store *store) GetUserByResetPasswordToken(ctx context.Context, token string) (*usertypes.StorableUser, error) {
user := new(usertypes.StorableUser)
err := store.
sqlstore.
@@ -685,8 +685,8 @@ func (store *store) GetUserByResetPasswordToken(ctx context.Context, token strin
return user, nil
}
func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, emails []string, statuses []string) ([]*usertypes.User, error) {
users := []*usertypes.User{}
func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, emails []string, statuses []string) ([]*usertypes.StorableUser, error) {
users := []*usertypes.StorableUser{}
err := store.
sqlstore.
@@ -704,8 +704,8 @@ func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID
return users, nil
}
func (store *store) GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*usertypes.User, *usertypes.FactorPassword, error) {
user := new(usertypes.User)
func (store *store) GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*usertypes.StorableUser, *usertypes.FactorPassword, error) {
user := new(usertypes.StorableUser)
factorPassword := new(usertypes.FactorPassword)
err := store.

View File

@@ -18,12 +18,12 @@ type funnel struct {
types.Identifiable // funnel id
types.TimeAuditable
types.UserAuditable
Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name
Description string `json:"description" bun:"description,type:text"` // funnel description
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []funnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *usertypes.User `json:"user" bun:"rel:belongs-to,join:created_by=id"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name
Description string `json:"description" bun:"description,type:text"` // funnel description
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []funnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *usertypes.StorableUser `json:"user" bun:"rel:belongs-to,join:created_by=id"`
}
type funnelStep struct {

View File

@@ -34,7 +34,7 @@ func (store *store) Create(ctx context.Context, token *authtypes.StorableToken)
}
func (store *store) GetIdentityByUserID(ctx context.Context, userID valuer.UUID) (*authtypes.Identity, error) {
user := new(usertypes.User)
user := new(usertypes.StorableUser)
err := store.
sqlstore.
@@ -47,7 +47,7 @@ func (store *store) GetIdentityByUserID(ctx context.Context, userID valuer.UUID)
return nil, store.sqlstore.WrapNotFoundErrf(err, usertypes.ErrCodeUserNotFound, "user with id: %s does not exist", userID)
}
return authtypes.NewIdentity(userID, user.OrgID, user.Email, authtypes.LegacyRole(user.Role), authtypes.IdentNProviderTokenizer), nil
return authtypes.NewIdentity(userID, valuer.MustNewUUID(user.OrgID), valuer.MustNewEmail(user.Email), authtypes.LegacyRole(user.Role), authtypes.IdentNProviderTokenizer), nil
}
func (store *store) GetByAccessToken(ctx context.Context, accessToken string) (*authtypes.StorableToken, error) {

View File

@@ -19,12 +19,12 @@ type StorableFunnel struct {
types.TimeAuditable
types.UserAuditable
bun.BaseModel `bun:"table:trace_funnel"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"`
Description string `json:"description" bun:"description,type:text"`
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []*FunnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *usertypes.User `json:"user" bun:"rel:belongs-to,join:created_by=id"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"`
Description string `json:"description" bun:"description,type:text"`
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []*FunnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *usertypes.StorableUser `json:"user" bun:"rel:belongs-to,join:created_by=id"`
}
type FunnelStep struct {

View File

@@ -108,7 +108,7 @@ func ConstructFunnelResponse(funnel *StorableFunnel, claims *authtypes.Claims) G
}
if funnel.CreatedByUser != nil {
resp.UserEmail = funnel.CreatedByUser.Email.String()
resp.UserEmail = funnel.CreatedByUser.Email
} else if claims != nil {
resp.UserEmail = claims.Email
}

View File

@@ -444,11 +444,11 @@ func TestConstructFunnelResponse(t *testing.T) {
},
Name: "test-funnel",
OrgID: orgID,
CreatedByUser: &usertypes.User{
CreatedByUser: &usertypes.StorableUser{
Identifiable: types.Identifiable{
ID: userID,
},
Email: valuer.MustNewEmail("funnel@example.com"),
Email: "funnel@example.com",
},
Steps: []*FunnelStep{
{

View File

@@ -41,15 +41,15 @@ type OrgUserAPIKey struct {
}
type UserWithAPIKey struct {
*User `bun:",extend"`
APIKeys []*StorableAPIKeyUser `bun:"rel:has-many,join:id=user_id"`
*StorableUser `bun:",extend"`
APIKeys []*StorableAPIKeyUser `bun:"rel:has-many,join:id=user_id"`
}
type StorableAPIKeyUser struct {
StorableAPIKey `bun:",extend"`
CreatedByUser *User `json:"createdByUser" bun:"created_by_user,rel:belongs-to,join:created_by=id"`
UpdatedByUser *User `json:"updatedByUser" bun:"updated_by_user,rel:belongs-to,join:updated_by=id"`
CreatedByUser *StorableUser `json:"createdByUser" bun:"created_by_user,rel:belongs-to,join:created_by=id"`
UpdatedByUser *StorableUser `json:"updatedByUser" bun:"updated_by_user,rel:belongs-to,join:updated_by=id"`
}
type StorableAPIKey struct {
@@ -140,7 +140,7 @@ func NewGettableAPIKeyFromStorableAPIKey(storableAPIKey *StorableAPIKeyUser) *Ge
LastUsed: lastUsed,
Revoked: storableAPIKey.Revoked,
UserID: storableAPIKey.UserID.String(),
CreatedByUser: storableAPIKey.CreatedByUser,
UpdatedByUser: storableAPIKey.UpdatedByUser,
CreatedByUser: NewUserFromStorable(storableAPIKey.CreatedByUser),
UpdatedByUser: NewUserFromStorable(storableAPIKey.UpdatedByUser),
}
}

View File

@@ -35,16 +35,26 @@ var (
ValidUserStatus = []valuer.String{UserStatusPendingInvite, UserStatusActive, UserStatusDeleted}
)
type GettableUser = User
type User struct {
types.Identifiable
DisplayName string `json:"displayName"`
Email valuer.Email `json:"email"`
Role authtypes.LegacyRole `json:"role"` // this will be moved to roles
OrgID valuer.UUID `json:"orgId"`
IsRoot bool `json:"isRoot"`
Status valuer.String `json:"status"`
DeletedAt time.Time `json:"-"`
types.TimeAuditable
}
type StorableUser struct {
bun.BaseModel `bun:"table:users"`
types.Identifiable
DisplayName string `bun:"display_name" json:"displayName"`
Email valuer.Email `bun:"email" json:"email"`
Role authtypes.LegacyRole `bun:"role" json:"role"`
OrgID valuer.UUID `bun:"org_id" json:"orgId"`
Email string `bun:"email" json:"email"`
Role authtypes.LegacyRole `bun:"role" json:"role"` // this will be removed as column from here
OrgID string `bun:"org_id" json:"orgId"`
IsRoot bool `bun:"is_root" json:"isRoot"`
Status valuer.String `bun:"status" json:"status"`
DeletedAt time.Time `bun:"deleted_at" json:"-"`
@@ -59,6 +69,60 @@ type PostableRegisterOrgAndAdmin struct {
OrgName string `json:"orgName"`
}
type UpdatableUser struct {
DisplayName string `json:"displayName" required:"true"`
Role string `json:"role" required:"true" nullable:"false"`
}
func NewStorableUser(user *User) *StorableUser {
if user == nil {
return nil
}
return &StorableUser{
Identifiable: user.Identifiable,
DisplayName: user.DisplayName,
Email: user.Email.String(),
Role: user.Role,
OrgID: user.OrgID.String(),
IsRoot: user.IsRoot,
Status: user.Status,
TimeAuditable: user.TimeAuditable,
}
}
func NewUserFromStorable(storableUser *StorableUser) *User {
if storableUser == nil {
return nil
}
return &User{
Identifiable: storableUser.Identifiable,
DisplayName: storableUser.DisplayName,
Email: valuer.MustNewEmail(storableUser.Email),
Role: storableUser.Role,
OrgID: valuer.MustNewUUID(storableUser.OrgID),
IsRoot: storableUser.IsRoot,
Status: storableUser.Status,
TimeAuditable: storableUser.TimeAuditable,
}
}
func NewUsersFromStorables(storableUsers []*StorableUser) []*User {
users := make([]*User, len(storableUsers))
for i, s := range storableUsers {
users[i] = NewUserFromStorable(s)
}
return users
}
func NewStorableUsers(users []*User) []*StorableUser {
storableUsers := make([]*StorableUser, len(users))
for i, u := range users {
storableUsers[i] = NewStorableUser(u)
}
return storableUsers
}
func NewUser(displayName string, email valuer.Email, role authtypes.LegacyRole, orgID valuer.UUID, status valuer.String) (*User, error) {
if email.IsZero() {
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "email is required")
@@ -217,33 +281,33 @@ func (request *PostableRegisterOrgAndAdmin) UnmarshalJSON(data []byte) error {
type UserStore interface {
// Creates a user.
CreateUser(ctx context.Context, user *User) error
CreateUser(ctx context.Context, user *StorableUser) error
// Get user by id.
GetUser(context.Context, valuer.UUID) (*User, error)
GetUser(context.Context, valuer.UUID) (*StorableUser, error)
// Get user by orgID and id.
GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*User, error)
GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*StorableUser, error)
// Get user by email and orgID.
GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*User, error)
GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*StorableUser, error)
// Get users by email.
GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*User, error)
GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*StorableUser, error)
// Get users by role and org.
GetActiveUsersByRoleAndOrgID(ctx context.Context, role authtypes.LegacyRole, orgID valuer.UUID) ([]*User, error)
GetActiveUsersByRoleAndOrgID(ctx context.Context, role authtypes.LegacyRole, orgID valuer.UUID) ([]*StorableUser, error)
// List users by org.
ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*User, error)
ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*StorableUser, error)
// List users by email and org ids.
ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*User, error)
ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*StorableUser, error)
// Get users for an org id using emails and statuses
GetUsersByEmailsOrgIDAndStatuses(context.Context, valuer.UUID, []string, []string) ([]*User, error)
GetUsersByEmailsOrgIDAndStatuses(context.Context, valuer.UUID, []string, []string) ([]*StorableUser, error)
UpdateUser(ctx context.Context, orgID valuer.UUID, user *User) error
UpdateUser(ctx context.Context, orgID valuer.UUID, user *StorableUser) error
DeleteUser(ctx context.Context, orgID string, id string) error
SoftDeleteUser(ctx context.Context, orgID string, id string) error
@@ -269,13 +333,13 @@ type UserStore interface {
CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error)
// Get root user by org.
GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*User, error)
GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*StorableUser, error)
// Get user by reset password token
GetUserByResetPasswordToken(ctx context.Context, token string) (*User, error)
GetUserByResetPasswordToken(ctx context.Context, token string) (*StorableUser, error)
// For AuthN - Get user and factor password by email and orgID.
GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*User, *FactorPassword, error)
GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*StorableUser, *FactorPassword, error)
// Transaction
RunInTx(ctx context.Context, cb func(ctx context.Context) error) error