mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:17:32 +00:00
feat: user query (#3075)
* user queries * user query * user query * user tests * remove old code * user metadata * cleanup * fix merge * cleanup * cleanup * fixes
This commit is contained in:
@@ -2,11 +2,100 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/lib/pq"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/query/projection"
|
||||
)
|
||||
|
||||
type Users struct {
|
||||
SearchResponse
|
||||
Users []*User
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
State domain.UserState
|
||||
Type domain.UserType
|
||||
Username string
|
||||
LoginNames []string
|
||||
PreferredLoginName string
|
||||
Human *Human
|
||||
Machine *Machine
|
||||
}
|
||||
|
||||
type Human struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
Email string
|
||||
IsEmailVerified bool
|
||||
Phone string
|
||||
IsPhoneVerified bool
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
Email string
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
type Phone struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
Phone string
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
type UserSearchQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
}
|
||||
|
||||
var (
|
||||
userTable = table{
|
||||
name: projection.UserTable,
|
||||
@@ -43,6 +132,13 @@ var (
|
||||
name: projection.UserTypeCol,
|
||||
table: userTable,
|
||||
}
|
||||
userLoginNamesTable = loginNameTable.setAlias("login_names")
|
||||
userLoginNamesUserIDCol = LoginNameUserIDCol.setTable(userLoginNamesTable)
|
||||
userLoginNamesCol = LoginNameNameCol.setTable(userLoginNamesTable)
|
||||
userPreferredLoginNameTable = loginNameTable.setAlias("preferred_login_name")
|
||||
userPreferredLoginNameUserIDCol = LoginNameUserIDCol.setTable(userPreferredLoginNameTable)
|
||||
userPreferredLoginNameCol = LoginNameNameCol.setTable(userPreferredLoginNameTable)
|
||||
userPreferredLoginNameIsPrimaryCol = LoginNameIsPrimaryCol.setTable(userPreferredLoginNameTable)
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -78,8 +174,8 @@ var (
|
||||
name: projection.HumanGenderCol,
|
||||
table: humanTable,
|
||||
}
|
||||
HumanAvaterURLCol = Column{
|
||||
name: projection.HumanAvaterURLCol,
|
||||
HumanAvatarURLCol = Column{
|
||||
name: projection.HumanAvatarURLCol,
|
||||
table: humanTable,
|
||||
}
|
||||
|
||||
@@ -122,7 +218,693 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, userID string, queries ...SearchQuery) (*User, error) {
|
||||
query, scan := prepareUserQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(
|
||||
sq.Eq{
|
||||
UserIDCol.identifier(): userID,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-FBg21", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, queries ...SearchQuery) (*User, error) {
|
||||
query, scan := prepareUserQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dnhr2", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanProfile(ctx context.Context, userID string, queries ...SearchQuery) (*Profile, error) {
|
||||
query, scan := prepareProfileQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(
|
||||
sq.Eq{
|
||||
UserIDCol.identifier(): userID,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dgbg2", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanEmail(ctx context.Context, userID string, queries ...SearchQuery) (*Email, error) {
|
||||
query, scan := prepareEmailQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(
|
||||
sq.Eq{
|
||||
UserIDCol.identifier(): userID,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-BHhj3", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanPhone(ctx context.Context, userID string, queries ...SearchQuery) (*Phone, error) {
|
||||
query, scan := preparePhoneQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(
|
||||
sq.Eq{
|
||||
UserIDCol.identifier(): userID,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries) (*Users, error) {
|
||||
query, scan := prepareUsersQuery()
|
||||
stmt, args, err := queries.toQuery(query).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dgbg2", "Errors.Query.SQLStatment")
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-AG4gs", "Errors.Internal")
|
||||
}
|
||||
users, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users.LatestSequence, err = q.latestSequence(ctx, userTable)
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwner string) (bool, error) {
|
||||
query, scan := prepareUserUniqueQuery()
|
||||
queries := make([]SearchQuery, 0, 3)
|
||||
if username != "" {
|
||||
usernameQuery, err := NewUserUsernameSearchQuery(username, TextEquals)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
queries = append(queries, usernameQuery)
|
||||
}
|
||||
if email != "" {
|
||||
emailQuery, err := NewUserEmailSearchQuery(email, TextEquals)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
queries = append(queries, emailQuery)
|
||||
}
|
||||
if resourceOwner != "" {
|
||||
resourceOwnerQuery, err := NewUserResourceOwnerSearchQuery(resourceOwner, TextEquals)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
queries = append(queries, resourceOwnerQuery)
|
||||
}
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return false, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment")
|
||||
}
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) UserEvents(ctx context.Context, orgID, userID string, sequence uint64) ([]eventstore.Event, error) {
|
||||
query := NewUserEventSearchQuery(userID, orgID, sequence)
|
||||
return q.eventstore.Filter(ctx, query)
|
||||
}
|
||||
|
||||
func (q *UserSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = q.SearchRequest.toQuery(query)
|
||||
for _, q := range q.Queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (r *UserSearchQueries) AppendMyResourceOwnerQuery(orgID string) error {
|
||||
query, err := NewUserResourceOwnerSearchQuery(orgID, TextEquals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Queries = append(r.Queries, query)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewUserResourceOwnerSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(UserResourceOwnerCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserUsernameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(UserUsernameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserFirstNameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanFirstNameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserLastNameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanLastNameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserNickNameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanNickNameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserDisplayNameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanDisplayNameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserEmailSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanEmailCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserStateSearchQuery(value int32) (SearchQuery, error) {
|
||||
return NewNumberQuery(UserStateCol, value, NumberEquals)
|
||||
}
|
||||
|
||||
func NewUserTypeSearchQuery(value int32) (SearchQuery, error) {
|
||||
return NewNumberQuery(UserTypeCol, value, NumberEquals)
|
||||
}
|
||||
|
||||
func NewUserPreferredLoginNameSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(userPreferredLoginNameCol, value, comparison)
|
||||
}
|
||||
|
||||
func NewUserLoginNamesSearchQuery(value string) (SearchQuery, error) {
|
||||
return NewTextQuery(userLoginNamesCol, value, TextListContains)
|
||||
}
|
||||
|
||||
func prepareUserQuery() (sq.SelectBuilder, func(*sql.Row) (*User, error)) {
|
||||
loginNamesQuery, _, err := sq.Select(
|
||||
userLoginNamesUserIDCol.identifier(),
|
||||
"ARRAY_AGG("+userLoginNamesCol.identifier()+") as login_names").
|
||||
From(userLoginNamesTable.identifier()).
|
||||
GroupBy(userLoginNamesUserIDCol.identifier()).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return sq.SelectBuilder{}, nil
|
||||
}
|
||||
preferredLoginNameQuery, preferredLoginNameArgs, err := sq.Select(
|
||||
userPreferredLoginNameUserIDCol.identifier(),
|
||||
userPreferredLoginNameCol.identifier()).
|
||||
From(userPreferredLoginNameTable.identifier()).
|
||||
Where(
|
||||
sq.Eq{
|
||||
userPreferredLoginNameIsPrimaryCol.identifier(): true,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return sq.SelectBuilder{}, nil
|
||||
}
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserCreationDateCol.identifier(),
|
||||
UserChangeDateCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
UserSequenceCol.identifier(),
|
||||
UserStateCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
"login_names.login_names",
|
||||
userPreferredLoginNameCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanNickNameCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanPreferredLanguageCol.identifier(),
|
||||
HumanGenderCol.identifier(),
|
||||
HumanAvatarURLCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanIsEmailVerifiedCol.identifier(),
|
||||
HumanPhoneCol.identifier(),
|
||||
HumanIsPhoneVerifiedCol.identifier(),
|
||||
MachineUserIDCol.identifier(),
|
||||
MachineNameCol.identifier(),
|
||||
MachineDescriptionCol.identifier(),
|
||||
).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
LeftJoin(join(MachineUserIDCol, UserIDCol)).
|
||||
LeftJoin("("+loginNamesQuery+") as login_names on "+userLoginNamesUserIDCol.identifier()+" = "+UserIDCol.identifier()).
|
||||
LeftJoin("("+preferredLoginNameQuery+") as preferred_login_name on "+userPreferredLoginNameUserIDCol.identifier()+" = "+UserIDCol.identifier(), preferredLoginNameArgs...).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*User, error) {
|
||||
u := new(User)
|
||||
loginNames := pq.StringArray{}
|
||||
preferredLoginName := sql.NullString{}
|
||||
|
||||
humanID := sql.NullString{}
|
||||
firstName := sql.NullString{}
|
||||
lastName := sql.NullString{}
|
||||
nickName := sql.NullString{}
|
||||
displayName := sql.NullString{}
|
||||
preferredLanguage := sql.NullString{}
|
||||
gender := sql.NullInt32{}
|
||||
avatarKey := sql.NullString{}
|
||||
email := sql.NullString{}
|
||||
isEmailVerified := sql.NullBool{}
|
||||
phone := sql.NullString{}
|
||||
isPhoneVerified := sql.NullBool{}
|
||||
|
||||
machineID := sql.NullString{}
|
||||
name := sql.NullString{}
|
||||
description := sql.NullString{}
|
||||
|
||||
err := row.Scan(
|
||||
&u.ID,
|
||||
&u.CreationDate,
|
||||
&u.ChangeDate,
|
||||
&u.ResourceOwner,
|
||||
&u.Sequence,
|
||||
&u.State,
|
||||
&u.Type,
|
||||
&u.Username,
|
||||
&loginNames,
|
||||
&preferredLoginName,
|
||||
&humanID,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&nickName,
|
||||
&displayName,
|
||||
&preferredLanguage,
|
||||
&gender,
|
||||
&avatarKey,
|
||||
&email,
|
||||
&isEmailVerified,
|
||||
&phone,
|
||||
&isPhoneVerified,
|
||||
&machineID,
|
||||
&name,
|
||||
&description,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-Dfbg2", "Errors.User.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Bgah2", "Errors.Internal")
|
||||
}
|
||||
|
||||
u.LoginNames = loginNames
|
||||
if preferredLoginName.Valid {
|
||||
u.PreferredLoginName = preferredLoginName.String
|
||||
}
|
||||
if humanID.Valid {
|
||||
u.Human = &Human{
|
||||
FirstName: firstName.String,
|
||||
LastName: lastName.String,
|
||||
NickName: nickName.String,
|
||||
DisplayName: displayName.String,
|
||||
AvatarKey: avatarKey.String,
|
||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||
Gender: domain.Gender(gender.Int32),
|
||||
Email: email.String,
|
||||
IsEmailVerified: isEmailVerified.Bool,
|
||||
Phone: phone.String,
|
||||
IsPhoneVerified: isPhoneVerified.Bool,
|
||||
}
|
||||
} else if machineID.Valid {
|
||||
u.Machine = &Machine{
|
||||
Name: name.String,
|
||||
Description: description.String,
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareProfileQuery() (sq.SelectBuilder, func(*sql.Row) (*Profile, error)) {
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserCreationDateCol.identifier(),
|
||||
UserChangeDateCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
UserSequenceCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanNickNameCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanPreferredLanguageCol.identifier(),
|
||||
HumanGenderCol.identifier(),
|
||||
HumanAvatarURLCol.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*Profile, error) {
|
||||
p := new(Profile)
|
||||
|
||||
humanID := sql.NullString{}
|
||||
firstName := sql.NullString{}
|
||||
lastName := sql.NullString{}
|
||||
nickName := sql.NullString{}
|
||||
displayName := sql.NullString{}
|
||||
preferredLanguage := sql.NullString{}
|
||||
gender := sql.NullInt32{}
|
||||
avatarKey := sql.NullString{}
|
||||
err := row.Scan(
|
||||
&p.ID,
|
||||
&p.CreationDate,
|
||||
&p.ChangeDate,
|
||||
&p.ResourceOwner,
|
||||
&p.Sequence,
|
||||
&humanID,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&nickName,
|
||||
&displayName,
|
||||
&preferredLanguage,
|
||||
&gender,
|
||||
&avatarKey,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-HNhb3", "Errors.User.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Rfheq", "Errors.Internal")
|
||||
}
|
||||
if !humanID.Valid {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-WLTce", "Errors.User.NotHuman")
|
||||
}
|
||||
|
||||
p.FirstName = firstName.String
|
||||
p.LastName = lastName.String
|
||||
p.NickName = nickName.String
|
||||
p.DisplayName = displayName.String
|
||||
p.AvatarKey = avatarKey.String
|
||||
p.PreferredLanguage = language.Make(preferredLanguage.String)
|
||||
p.Gender = domain.Gender(gender.Int32)
|
||||
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareEmailQuery() (sq.SelectBuilder, func(*sql.Row) (*Email, error)) {
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserCreationDateCol.identifier(),
|
||||
UserChangeDateCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
UserSequenceCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanIsEmailVerifiedCol.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*Email, error) {
|
||||
e := new(Email)
|
||||
|
||||
humanID := sql.NullString{}
|
||||
email := sql.NullString{}
|
||||
isEmailVerified := sql.NullBool{}
|
||||
|
||||
err := row.Scan(
|
||||
&e.ID,
|
||||
&e.CreationDate,
|
||||
&e.ChangeDate,
|
||||
&e.ResourceOwner,
|
||||
&e.Sequence,
|
||||
&humanID,
|
||||
&email,
|
||||
&isEmailVerified,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-Hms2s", "Errors.User.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Nu42d", "Errors.Internal")
|
||||
}
|
||||
if !humanID.Valid {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-pt7HY", "Errors.User.NotHuman")
|
||||
}
|
||||
|
||||
e.Email = email.String
|
||||
e.IsVerified = isEmailVerified.Bool
|
||||
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
func preparePhoneQuery() (sq.SelectBuilder, func(*sql.Row) (*Phone, error)) {
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserCreationDateCol.identifier(),
|
||||
UserChangeDateCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
UserSequenceCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanPhoneCol.identifier(),
|
||||
HumanIsPhoneVerifiedCol.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*Phone, error) {
|
||||
e := new(Phone)
|
||||
|
||||
humanID := sql.NullString{}
|
||||
phone := sql.NullString{}
|
||||
isPhoneVerified := sql.NullBool{}
|
||||
|
||||
err := row.Scan(
|
||||
&e.ID,
|
||||
&e.CreationDate,
|
||||
&e.ChangeDate,
|
||||
&e.ResourceOwner,
|
||||
&e.Sequence,
|
||||
&humanID,
|
||||
&phone,
|
||||
&isPhoneVerified,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-DAvb3", "Errors.User.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Bmf2h", "Errors.Internal")
|
||||
}
|
||||
if !humanID.Valid {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-hliQl", "Errors.User.NotHuman")
|
||||
}
|
||||
|
||||
e.Phone = phone.String
|
||||
e.IsVerified = isPhoneVerified.Bool
|
||||
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUserUniqueQuery() (sq.SelectBuilder, func(*sql.Row) (bool, error)) {
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserStateCol.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanIsEmailVerifiedCol.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (bool, error) {
|
||||
userID := sql.NullString{}
|
||||
state := sql.NullInt32{}
|
||||
username := sql.NullString{}
|
||||
humanID := sql.NullString{}
|
||||
email := sql.NullString{}
|
||||
isEmailVerified := sql.NullBool{}
|
||||
|
||||
err := row.Scan(
|
||||
&userID,
|
||||
&state,
|
||||
&username,
|
||||
&humanID,
|
||||
&email,
|
||||
&isEmailVerified,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.ThrowInternal(err, "QUERY-Cxces", "Errors.Internal")
|
||||
}
|
||||
return !userID.Valid, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUsersQuery() (sq.SelectBuilder, func(*sql.Rows) (*Users, error)) {
|
||||
loginNamesQuery, _, err := sq.Select(
|
||||
userLoginNamesUserIDCol.identifier(),
|
||||
"ARRAY_AGG("+userLoginNamesCol.identifier()+") as login_names").
|
||||
From(userLoginNamesTable.identifier()).
|
||||
GroupBy(userLoginNamesUserIDCol.identifier()).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return sq.SelectBuilder{}, nil
|
||||
}
|
||||
preferredLoginNameQuery, preferredLoginNameArgs, err := sq.Select(
|
||||
userPreferredLoginNameUserIDCol.identifier(),
|
||||
userPreferredLoginNameCol.identifier()).
|
||||
From(userPreferredLoginNameTable.identifier()).
|
||||
Where(
|
||||
sq.Eq{
|
||||
userPreferredLoginNameIsPrimaryCol.identifier(): true,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return sq.SelectBuilder{}, nil
|
||||
}
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
UserCreationDateCol.identifier(),
|
||||
UserChangeDateCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
UserSequenceCol.identifier(),
|
||||
UserStateCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
"login_names.login_names",
|
||||
userPreferredLoginNameCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanNickNameCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanPreferredLanguageCol.identifier(),
|
||||
HumanGenderCol.identifier(),
|
||||
HumanAvatarURLCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanIsEmailVerifiedCol.identifier(),
|
||||
HumanPhoneCol.identifier(),
|
||||
HumanIsPhoneVerifiedCol.identifier(),
|
||||
MachineUserIDCol.identifier(),
|
||||
MachineNameCol.identifier(),
|
||||
MachineDescriptionCol.identifier(),
|
||||
countColumn.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
LeftJoin(join(MachineUserIDCol, UserIDCol)).
|
||||
LeftJoin("("+loginNamesQuery+") as login_names on "+userLoginNamesUserIDCol.identifier()+" = "+UserIDCol.identifier()).
|
||||
LeftJoin("("+preferredLoginNameQuery+") as preferred_login_name on "+userPreferredLoginNameUserIDCol.identifier()+" = "+UserIDCol.identifier(), preferredLoginNameArgs...).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) (*Users, error) {
|
||||
users := make([]*User, 0)
|
||||
var count uint64
|
||||
for rows.Next() {
|
||||
u := new(User)
|
||||
loginNames := pq.StringArray{}
|
||||
preferredLoginName := sql.NullString{}
|
||||
|
||||
humanID := sql.NullString{}
|
||||
firstName := sql.NullString{}
|
||||
lastName := sql.NullString{}
|
||||
nickName := sql.NullString{}
|
||||
displayName := sql.NullString{}
|
||||
preferredLanguage := sql.NullString{}
|
||||
gender := sql.NullInt32{}
|
||||
avatarKey := sql.NullString{}
|
||||
email := sql.NullString{}
|
||||
isEmailVerified := sql.NullBool{}
|
||||
phone := sql.NullString{}
|
||||
isPhoneVerified := sql.NullBool{}
|
||||
|
||||
machineID := sql.NullString{}
|
||||
name := sql.NullString{}
|
||||
description := sql.NullString{}
|
||||
|
||||
err := rows.Scan(
|
||||
&u.ID,
|
||||
&u.CreationDate,
|
||||
&u.ChangeDate,
|
||||
&u.ResourceOwner,
|
||||
&u.Sequence,
|
||||
&u.State,
|
||||
&u.Type,
|
||||
&u.Username,
|
||||
&loginNames,
|
||||
&preferredLoginName,
|
||||
&humanID,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&nickName,
|
||||
&displayName,
|
||||
&preferredLanguage,
|
||||
&gender,
|
||||
&avatarKey,
|
||||
&email,
|
||||
&isEmailVerified,
|
||||
&phone,
|
||||
&isPhoneVerified,
|
||||
&machineID,
|
||||
&name,
|
||||
&description,
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.LoginNames = loginNames
|
||||
if preferredLoginName.Valid {
|
||||
u.PreferredLoginName = preferredLoginName.String
|
||||
}
|
||||
|
||||
if humanID.Valid {
|
||||
u.Human = &Human{
|
||||
FirstName: firstName.String,
|
||||
LastName: lastName.String,
|
||||
NickName: nickName.String,
|
||||
DisplayName: displayName.String,
|
||||
AvatarKey: avatarKey.String,
|
||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||
Gender: domain.Gender(gender.Int32),
|
||||
Email: email.String,
|
||||
IsEmailVerified: isEmailVerified.Bool,
|
||||
Phone: phone.String,
|
||||
IsPhoneVerified: isPhoneVerified.Bool,
|
||||
}
|
||||
} else if machineID.Valid {
|
||||
u.Machine = &Machine{
|
||||
Name: name.String,
|
||||
Description: description.String,
|
||||
}
|
||||
}
|
||||
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-frhbd", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return &Users{
|
||||
Users: users,
|
||||
SearchResponse: SearchResponse{
|
||||
Count: count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user