mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
fix(notify): notify user in projection (#3889)
* start implement notify user in projection * fix(stmt): add copy to multi stmt * use projections for notify users * feat: notifications from projections * feat: notifications from projections * cleanup * pre-release * fix tests * fix types * fix command * fix queryNotifyUser * fix: build version * fix: HumanPasswordlessInitCodeSent Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
This commit is contained in:
@@ -11,9 +11,7 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
)
|
||||
@@ -92,6 +90,31 @@ type Machine struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
type NotifyUser 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
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
LastEmail string
|
||||
VerifiedEmail string
|
||||
LastPhone string
|
||||
VerifiedPhone string
|
||||
PasswordSet bool
|
||||
}
|
||||
|
||||
type UserSearchQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
@@ -237,6 +260,38 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
notifyTable = table{
|
||||
name: projection.UserNotifyTable,
|
||||
}
|
||||
NotifyUserIDCol = Column{
|
||||
name: projection.NotifyUserIDCol,
|
||||
table: notifyTable,
|
||||
}
|
||||
NotifyEmailCol = Column{
|
||||
name: projection.NotifyLastEmailCol,
|
||||
table: notifyTable,
|
||||
isOrderByLower: true,
|
||||
}
|
||||
NotifyVerifiedEmailCol = Column{
|
||||
name: projection.NotifyVerifiedEmailCol,
|
||||
table: notifyTable,
|
||||
isOrderByLower: true,
|
||||
}
|
||||
NotifyPhoneCol = Column{
|
||||
name: projection.NotifyLastPhoneCol,
|
||||
table: notifyTable,
|
||||
}
|
||||
NotifyVerifiedPhoneCol = Column{
|
||||
name: projection.NotifyVerifiedPhoneCol,
|
||||
table: notifyTable,
|
||||
}
|
||||
NotifyPasswordSetCol = Column{
|
||||
name: projection.NotifyPasswordSetCol,
|
||||
table: notifyTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggered bool, userID string, queries ...SearchQuery) (*User, error) {
|
||||
if shouldTriggered {
|
||||
projection.UserProjection.TriggerBulk(ctx)
|
||||
@@ -327,6 +382,28 @@ func (q *Queries) GetHumanPhone(ctx context.Context, userID string, queries ...S
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GeNotifyUser(ctx context.Context, shouldTriggered bool, userID string, queries ...SearchQuery) (*NotifyUser, error) {
|
||||
if shouldTriggered {
|
||||
projection.UserProjection.TriggerBulk(ctx)
|
||||
}
|
||||
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
query, scan := prepareNotifyUserQuery(instanceID)
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(sq.Eq{
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): instanceID,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Err3g", "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).
|
||||
@@ -748,6 +825,143 @@ func preparePhoneQuery() (sq.SelectBuilder, func(*sql.Row) (*Phone, error)) {
|
||||
}
|
||||
}
|
||||
|
||||
func prepareNotifyUserQuery(instanceID string) (sq.SelectBuilder, func(*sql.Row) (*NotifyUser, error)) {
|
||||
loginNamesQuery, loginNamesArgs, err := sq.Select(
|
||||
userLoginNamesUserIDCol.identifier(),
|
||||
"ARRAY_AGG("+userLoginNamesNameCol.identifier()+") as "+userLoginNamesListCol.name).
|
||||
From(userLoginNamesTable.identifier()).
|
||||
GroupBy(userLoginNamesUserIDCol.identifier()).
|
||||
Where(sq.Eq{
|
||||
userLoginNamesInstanceIDCol.identifier(): instanceID,
|
||||
}).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,
|
||||
userPreferredLoginNameInstanceIDCol.identifier(): instanceID,
|
||||
}).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(),
|
||||
userLoginNamesListCol.identifier(),
|
||||
userPreferredLoginNameCol.identifier(),
|
||||
HumanUserIDCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanNickNameCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanPreferredLanguageCol.identifier(),
|
||||
HumanGenderCol.identifier(),
|
||||
HumanAvatarURLCol.identifier(),
|
||||
NotifyUserIDCol.identifier(),
|
||||
NotifyEmailCol.identifier(),
|
||||
NotifyVerifiedEmailCol.identifier(),
|
||||
NotifyPhoneCol.identifier(),
|
||||
NotifyVerifiedPhoneCol.identifier(),
|
||||
NotifyPasswordSetCol.identifier(),
|
||||
).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(HumanUserIDCol, UserIDCol)).
|
||||
LeftJoin(join(NotifyUserIDCol, UserIDCol)).
|
||||
LeftJoin("("+loginNamesQuery+") as "+userLoginNamesTable.alias+" on "+userLoginNamesUserIDCol.identifier()+" = "+UserIDCol.identifier(), loginNamesArgs...).
|
||||
LeftJoin("("+preferredLoginNameQuery+") as "+userPreferredLoginNameTable.alias+" on "+userPreferredLoginNameUserIDCol.identifier()+" = "+UserIDCol.identifier(), preferredLoginNameArgs...).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*NotifyUser, error) {
|
||||
u := new(NotifyUser)
|
||||
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{}
|
||||
|
||||
notifyUserID := sql.NullString{}
|
||||
notifyEmail := sql.NullString{}
|
||||
notifyVerifiedEmail := sql.NullString{}
|
||||
notifyPhone := sql.NullString{}
|
||||
notifyVerifiedPhone := sql.NullString{}
|
||||
notifyPasswordSet := sql.NullBool{}
|
||||
|
||||
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,
|
||||
¬ifyUserID,
|
||||
¬ifyEmail,
|
||||
¬ifyVerifiedEmail,
|
||||
¬ifyPhone,
|
||||
¬ifyVerifiedPhone,
|
||||
¬ifyPasswordSet,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-Dgqd2", "Errors.User.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dbwsg", "Errors.Internal")
|
||||
}
|
||||
|
||||
if !notifyUserID.Valid {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-Sfw3f", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
u.LoginNames = loginNames
|
||||
if preferredLoginName.Valid {
|
||||
u.PreferredLoginName = preferredLoginName.String
|
||||
}
|
||||
if humanID.Valid {
|
||||
u.FirstName = firstName.String
|
||||
u.LastName = lastName.String
|
||||
u.NickName = nickName.String
|
||||
u.DisplayName = displayName.String
|
||||
u.AvatarKey = avatarKey.String
|
||||
u.PreferredLanguage = language.Make(preferredLanguage.String)
|
||||
u.Gender = domain.Gender(gender.Int32)
|
||||
}
|
||||
u.LastEmail = notifyEmail.String
|
||||
u.VerifiedEmail = notifyVerifiedEmail.String
|
||||
u.LastPhone = notifyPhone.String
|
||||
u.VerifiedPhone = notifyVerifiedPhone.String
|
||||
u.PasswordSet = notifyPasswordSet.Bool
|
||||
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUserUniqueQuery() (sq.SelectBuilder, func(*sql.Row) (bool, error)) {
|
||||
return sq.Select(
|
||||
UserIDCol.identifier(),
|
||||
|
Reference in New Issue
Block a user