mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 22:57:31 +00:00
v4 without errors
This commit is contained in:
@@ -15,7 +15,7 @@ var (
|
|||||||
userCodeAlgorithm crypto.EncryptionAlgorithm
|
userCodeAlgorithm crypto.EncryptionAlgorithm
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
|
|
||||||
// userRepo func(database.QueryExecutor) UserRepository
|
userRepo func(database.QueryExecutor) UserRepository
|
||||||
instanceRepo func(database.QueryExecutor) InstanceRepository
|
instanceRepo func(database.QueryExecutor) InstanceRepository
|
||||||
cryptoRepo func(database.QueryExecutor) CryptoRepository
|
cryptoRepo func(database.QueryExecutor) CryptoRepository
|
||||||
orgRepo func(database.QueryExecutor) OrgRepository
|
orgRepo func(database.QueryExecutor) OrgRepository
|
||||||
@@ -39,9 +39,9 @@ func SetTracer(t tracing.Tracer) {
|
|||||||
tracer = t
|
tracer = t
|
||||||
}
|
}
|
||||||
|
|
||||||
// func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
||||||
// userRepo = repo
|
userRepo = repo
|
||||||
// }
|
}
|
||||||
|
|
||||||
func SetInstanceRepository(repo func(database.QueryExecutor) InstanceRepository) {
|
func SetInstanceRepository(repo func(database.QueryExecutor) InstanceRepository) {
|
||||||
instanceRepo = repo
|
instanceRepo = repo
|
||||||
|
@@ -2,8 +2,7 @@ package domain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
v4 "github.com/zitadel/zitadel/backend/v3/storage/database/repository/stmt/v4"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmailVerifiedCommand struct {
|
type EmailVerifiedCommand struct {
|
||||||
@@ -27,7 +26,8 @@ var (
|
|||||||
|
|
||||||
// Execute implements [Commander]
|
// Execute implements [Commander]
|
||||||
func (cmd *EmailVerifiedCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
func (cmd *EmailVerifiedCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
return userRepo(opts.DB).Human().ByID(cmd.UserID).Exec().SetEmailVerified(ctx, cmd.Email.Address)
|
repo := userRepo(opts.DB).Human()
|
||||||
|
return repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailVerifiedAt(time.Time{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyOnSetEmail implements [SetEmailOpt]
|
// applyOnSetEmail implements [SetEmailOpt]
|
||||||
@@ -78,8 +78,9 @@ func (cmd *SendCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts)
|
|||||||
if cmd.Email != "" {
|
if cmd.Email != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
email, err := userRepo(opts.DB).Human().ByID(cmd.UserID).Exec().GetEmail(ctx)
|
repo := userRepo(opts.DB).Human()
|
||||||
if err != nil || email.IsVerified {
|
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||||
|
if err != nil || !email.VerifiedAt.IsZero() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Email = email.Address
|
cmd.Email = email.Address
|
||||||
@@ -137,10 +138,9 @@ func (cmd *ReturnCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts
|
|||||||
if cmd.Email != "" {
|
if cmd.Email != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
user := v4.UserRepository(opts.DB)
|
repo := userRepo(opts.DB).Human()
|
||||||
user.WithCondition(user.IDCondition(cmd.UserID))
|
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||||
email, err := user.he.GetEmail(ctx)
|
if err != nil || !email.VerifiedAt.IsZero() {
|
||||||
if err != nil || email.IsVerified {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Email = email.Address
|
cmd.Email = email.Address
|
||||||
|
@@ -38,7 +38,8 @@ func (cmd *SetEmailCommand) Execute(ctx context.Context, opts *CommandOpts) erro
|
|||||||
}
|
}
|
||||||
defer func() { err = close(ctx, err) }()
|
defer func() { err = close(ctx, err) }()
|
||||||
// userStatement(opts.DB).Human().ByID(cmd.UserID).SetEmail(ctx, cmd.Email)
|
// userStatement(opts.DB).Human().ByID(cmd.UserID).SetEmail(ctx, cmd.Email)
|
||||||
err = userRepo(opts.DB).Human().ByID(cmd.UserID).Exec().SetEmail(ctx, cmd.Email)
|
repo := userRepo(opts.DB).Human()
|
||||||
|
err = repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailAddress(cmd.Email))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,6 @@ func (cmd *SetEmailCommand) Events() []*eventstore.Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// applyOnCreateHuman implements [CreateHumanOpt].
|
// applyOnCreateHuman implements [CreateHumanOpt].
|
||||||
func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand[Human]) {
|
func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand) {
|
||||||
createUserCmd.email = cmd
|
createUserCmd.email = cmd
|
||||||
}
|
}
|
||||||
|
@@ -9,13 +9,13 @@ import (
|
|||||||
|
|
||||||
type userColumns interface {
|
type userColumns interface {
|
||||||
// TODO: move v4.columns to domain
|
// TODO: move v4.columns to domain
|
||||||
InstanceIDColumn() column
|
InstanceIDColumn() v4.Column
|
||||||
OrgIDColumn() column
|
OrgIDColumn() v4.Column
|
||||||
IDColumn() column
|
IDColumn() v4.Column
|
||||||
usernameColumn() column
|
usernameColumn() v4.Column
|
||||||
CreatedAtColumn() column
|
CreatedAtColumn() v4.Column
|
||||||
UpdatedAtColumn() column
|
UpdatedAtColumn() v4.Column
|
||||||
DeletedAtColumn() column
|
DeletedAtColumn() v4.Column
|
||||||
}
|
}
|
||||||
|
|
||||||
type userConditions interface {
|
type userConditions interface {
|
||||||
@@ -29,30 +29,35 @@ type userConditions interface {
|
|||||||
DeletedAtCondition(op v4.NumberOperator, deletedAt time.Time) v4.Condition
|
DeletedAtCondition(op v4.NumberOperator, deletedAt time.Time) v4.Condition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type userChanges interface {
|
||||||
|
SetUsername(username string) v4.Change
|
||||||
|
}
|
||||||
|
|
||||||
type UserRepository interface {
|
type UserRepository interface {
|
||||||
userColumns
|
userColumns
|
||||||
userConditions
|
userConditions
|
||||||
|
userChanges
|
||||||
// TODO: move condition to domain
|
// TODO: move condition to domain
|
||||||
WithCondition(condition v4.Condition) UserRepository
|
Get(ctx context.Context, opts v4.QueryOption) (*User, error)
|
||||||
Get(ctx context.Context) (*User, error)
|
List(ctx context.Context, opts v4.QueryOption) ([]*User, error)
|
||||||
List(ctx context.Context) ([]*User, error)
|
Delete(ctx context.Context, condition v4.Condition) error
|
||||||
Create(ctx context.Context, user *User) error
|
|
||||||
Delete(ctx context.Context) error
|
|
||||||
|
|
||||||
Human() HumanRepository
|
Human() HumanRepository
|
||||||
Machine() MachineRepository
|
Machine() MachineRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
type humanColumns interface {
|
type humanColumns interface {
|
||||||
FirstNameColumn() column
|
userColumns
|
||||||
LastNameColumn() column
|
FirstNameColumn() v4.Column
|
||||||
EmailAddressColumn() column
|
LastNameColumn() v4.Column
|
||||||
EmailVerifiedAtColumn() column
|
EmailAddressColumn() v4.Column
|
||||||
PhoneNumberColumn() column
|
EmailVerifiedAtColumn() v4.Column
|
||||||
PhoneVerifiedAtColumn() column
|
PhoneNumberColumn() v4.Column
|
||||||
|
PhoneVerifiedAtColumn() v4.Column
|
||||||
}
|
}
|
||||||
|
|
||||||
type humanConditions interface {
|
type humanConditions interface {
|
||||||
|
userConditions
|
||||||
FirstNameCondition(op v4.TextOperator, firstName string) v4.Condition
|
FirstNameCondition(op v4.TextOperator, firstName string) v4.Condition
|
||||||
LastNameCondition(op v4.TextOperator, lastName string) v4.Condition
|
LastNameCondition(op v4.TextOperator, lastName string) v4.Condition
|
||||||
EmailAddressCondition(op v4.TextOperator, email string) v4.Condition
|
EmailAddressCondition(op v4.TextOperator, email string) v4.Condition
|
||||||
@@ -63,26 +68,53 @@ type humanConditions interface {
|
|||||||
PhoneVerifiedAtCondition(op v4.TextOperator, phoneVerifiedAt string) v4.Condition
|
PhoneVerifiedAtCondition(op v4.TextOperator, phoneVerifiedAt string) v4.Condition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type humanChanges interface {
|
||||||
|
userChanges
|
||||||
|
SetFirstName(firstName string) v4.Change
|
||||||
|
SetLastName(lastName string) v4.Change
|
||||||
|
|
||||||
|
SetEmail(address string, verified *time.Time) v4.Change
|
||||||
|
SetEmailAddress(email string) v4.Change
|
||||||
|
SetEmailVerifiedAt(emailVerifiedAt time.Time) v4.Change
|
||||||
|
|
||||||
|
SetPhone(number string, verifiedAt *time.Time) v4.Change
|
||||||
|
SetPhoneNumber(phoneNumber string) v4.Change
|
||||||
|
SetPhoneVerifiedAt(phoneVerifiedAt time.Time) v4.Change
|
||||||
|
}
|
||||||
|
|
||||||
type HumanRepository interface {
|
type HumanRepository interface {
|
||||||
humanColumns
|
humanColumns
|
||||||
humanConditions
|
humanConditions
|
||||||
|
humanChanges
|
||||||
|
|
||||||
GetEmail(ctx context.Context) (*Email, error)
|
GetEmail(ctx context.Context, condition v4.Condition) (*Email, error)
|
||||||
// TODO: replace any with add email update columns
|
// TODO: replace any with add email update columns
|
||||||
SetEmail(ctx context.Context, columns ...any) error
|
Create(ctx context.Context, user *User) error
|
||||||
|
Update(ctx context.Context, condition v4.Condition, changes ...v4.Change) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type machineColumns interface {
|
type machineColumns interface {
|
||||||
DescriptionColumn() column
|
userColumns
|
||||||
|
DescriptionColumn() v4.Column
|
||||||
}
|
}
|
||||||
|
|
||||||
type machineConditions interface {
|
type machineConditions interface {
|
||||||
|
userConditions
|
||||||
DescriptionCondition(op v4.TextOperator, description string) v4.Condition
|
DescriptionCondition(op v4.TextOperator, description string) v4.Condition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type machineChanges interface {
|
||||||
|
userChanges
|
||||||
|
SetDescription(description string) v4.Change
|
||||||
|
}
|
||||||
|
|
||||||
type MachineRepository interface {
|
type MachineRepository interface {
|
||||||
machineColumns
|
machineColumns
|
||||||
machineConditions
|
machineConditions
|
||||||
|
machineChanges
|
||||||
|
|
||||||
|
Create(ctx context.Context, user *User) error
|
||||||
|
Update(ctx context.Context, condition v4.Condition, changes ...v4.Change) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// type UserRepository interface {
|
// type UserRepository interface {
|
||||||
@@ -171,6 +203,11 @@ type User struct {
|
|||||||
v4.User
|
v4.User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
v4.Email
|
||||||
|
IsVerified bool
|
||||||
|
}
|
||||||
|
|
||||||
// type userTraits interface {
|
// type userTraits interface {
|
||||||
// isUserTraits()
|
// isUserTraits()
|
||||||
// }
|
// }
|
||||||
|
@@ -50,6 +50,17 @@ var _ Change = Changes(nil)
|
|||||||
|
|
||||||
var _ Change = (*change[string])(nil)
|
var _ Change = (*change[string])(nil)
|
||||||
|
|
||||||
|
type Columns []Column
|
||||||
|
|
||||||
|
func (m Columns) writeTo(builder *statementBuilder) {
|
||||||
|
for i, col := range m {
|
||||||
|
if i > 0 {
|
||||||
|
builder.WriteString(", ")
|
||||||
|
}
|
||||||
|
col.writeTo(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Column interface {
|
type Column interface {
|
||||||
writeTo(builder *statementBuilder)
|
writeTo(builder *statementBuilder)
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,8 @@ CREATE TABLE users (
|
|||||||
-- , CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
-- , CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||||
) INHERITS (org_objects);
|
) INHERITS (org_objects);
|
||||||
|
|
||||||
|
CREATE INDEX idx_users_username ON users(username);
|
||||||
|
|
||||||
CREATE TRIGGER set_updated_at
|
CREATE TRIGGER set_updated_at
|
||||||
BEFORE UPDATE
|
BEFORE UPDATE
|
||||||
ON users
|
ON users
|
||||||
@@ -74,6 +76,8 @@ CREATE TABLE human_users(
|
|||||||
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||||
) INHERITS (users);
|
) INHERITS (users);
|
||||||
|
|
||||||
|
CREATE INDEX idx_human_users_username ON human_users(username);
|
||||||
|
|
||||||
CREATE TRIGGER set_updated_at
|
CREATE TRIGGER set_updated_at
|
||||||
BEFORE UPDATE
|
BEFORE UPDATE
|
||||||
ON human_users
|
ON human_users
|
||||||
@@ -88,23 +92,15 @@ CREATE TABLE machine_users(
|
|||||||
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||||
) INHERITS (users);
|
) INHERITS (users);
|
||||||
|
|
||||||
|
CREATE INDEX idx_machine_users_username ON machine_users(username);
|
||||||
|
|
||||||
CREATE TRIGGER set_updated_at
|
CREATE TRIGGER set_updated_at
|
||||||
BEFORE UPDATE
|
BEFORE UPDATE
|
||||||
ON machine_users
|
ON machine_users
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION update_updated_at_column();
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE VIEW users_view AS (
|
||||||
select u.*, hu.first_name, hu.last_name, mu.description from users u
|
|
||||||
left join human_users hu on u.instance_id = hu.instance_id and u.org_id = hu.org_id and u.id = hu.id
|
|
||||||
left join machine_users mu on u.instance_id = mu.instance_id and u.org_id = mu.org_id and u.id = mu.id
|
|
||||||
-- where
|
|
||||||
-- u.instance_id = 1
|
|
||||||
-- and u.org_id = 3
|
|
||||||
-- and u.id = 7
|
|
||||||
;
|
|
||||||
|
|
||||||
create view users_view as (
|
|
||||||
SELECT
|
SELECT
|
||||||
id
|
id
|
||||||
, created_at
|
, created_at
|
||||||
@@ -113,27 +109,16 @@ SELECT
|
|||||||
, instance_id
|
, instance_id
|
||||||
, org_id
|
, org_id
|
||||||
, username
|
, username
|
||||||
, first_name
|
, tableoid::regclass::TEXT AS type
|
||||||
, last_name
|
|
||||||
, description
|
|
||||||
FROM (
|
|
||||||
(SELECT
|
|
||||||
id
|
|
||||||
, created_at
|
|
||||||
, updated_at
|
|
||||||
, deleted_at
|
|
||||||
, instance_id
|
|
||||||
, org_id
|
|
||||||
, username
|
|
||||||
, first_name
|
, first_name
|
||||||
, last_name
|
, last_name
|
||||||
, NULL AS description
|
, NULL AS description
|
||||||
FROM
|
FROM
|
||||||
human_users)
|
human_users
|
||||||
|
|
||||||
UNION
|
UNION
|
||||||
|
|
||||||
(SELECT
|
SELECT
|
||||||
id
|
id
|
||||||
, created_at
|
, created_at
|
||||||
, updated_at
|
, updated_at
|
||||||
@@ -141,9 +126,10 @@ UNION
|
|||||||
, instance_id
|
, instance_id
|
||||||
, org_id
|
, org_id
|
||||||
, username
|
, username
|
||||||
|
, tableoid::regclass::TEXT AS type
|
||||||
, NULL AS first_name
|
, NULL AS first_name
|
||||||
, NULL AS last_name
|
, NULL AS last_name
|
||||||
, description
|
, description
|
||||||
FROM
|
FROM
|
||||||
machine_users)
|
machine_users
|
||||||
));
|
);
|
66
backend/v3/storage/database/repository/stmt/v4/query.go
Normal file
66
backend/v3/storage/database/repository/stmt/v4/query.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
type queryOpts struct {
|
||||||
|
condition Condition
|
||||||
|
orderBy Columns
|
||||||
|
limit uint32
|
||||||
|
offset uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *queryOpts) writeCondition(builder *statementBuilder) {
|
||||||
|
if opts.condition == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
builder.WriteString(" WHERE ")
|
||||||
|
opts.condition.writeTo(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *queryOpts) writeOrderBy(builder *statementBuilder) {
|
||||||
|
if len(opts.orderBy) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
builder.WriteString(" ORDER BY ")
|
||||||
|
opts.orderBy.writeTo(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *queryOpts) writeLimit(builder *statementBuilder) {
|
||||||
|
if opts.limit == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
builder.WriteString(" LIMIT ")
|
||||||
|
builder.writeArg(opts.limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *queryOpts) writeOffset(builder *statementBuilder) {
|
||||||
|
if opts.offset == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
builder.WriteString(" OFFSET ")
|
||||||
|
builder.writeArg(opts.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryOption func(*queryOpts)
|
||||||
|
|
||||||
|
func WithCondition(condition Condition) QueryOption {
|
||||||
|
return func(opts *queryOpts) {
|
||||||
|
opts.condition = condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithOrderBy(orderBy ...Column) QueryOption {
|
||||||
|
return func(opts *queryOpts) {
|
||||||
|
opts.orderBy = orderBy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithLimit(limit uint32) QueryOption {
|
||||||
|
return func(opts *queryOpts) {
|
||||||
|
opts.limit = limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithOffset(offset uint32) QueryOption {
|
||||||
|
return func(opts *queryOpts) {
|
||||||
|
opts.offset = offset
|
||||||
|
}
|
||||||
|
}
|
@@ -29,17 +29,13 @@ type userTrait interface {
|
|||||||
Type() UserType
|
Type() UserType
|
||||||
}
|
}
|
||||||
|
|
||||||
const userQuery = `SELECT u.instance_id, u.org_id, u.id, u.username, u.type, u.created_at, u.updated_at, u.deleted_at,` +
|
const queryUserStmt = `SELECT instance_id, org_id, id, username, type, created_at, updated_at, deleted_at,` +
|
||||||
` h.first_name, h.last_name, h.email_address, h.email_verified_at, h.phone_number, h.phone_verified_at, m.description` +
|
` first_name, last_name, email_address, email_verified_at, phone_number, phone_verified_at, description` +
|
||||||
` FROM users u` +
|
` FROM users_view`
|
||||||
` LEFT JOIN user_humans h ON u.instance_id = h.instance_id AND u.org_id = h.org_id AND u.id = h.id` +
|
|
||||||
` LEFT JOIN user_machines m ON u.instance_id = m.instance_id AND u.org_id = m.org_id AND u.id = m.id`
|
|
||||||
|
|
||||||
type user struct {
|
type user struct {
|
||||||
builder statementBuilder
|
builder statementBuilder
|
||||||
client database.QueryExecutor
|
client database.QueryExecutor
|
||||||
|
|
||||||
condition Condition
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserRepository(client database.QueryExecutor) *user {
|
func UserRepository(client database.QueryExecutor) *user {
|
||||||
@@ -48,20 +44,17 @@ func UserRepository(client database.QueryExecutor) *user {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) WithCondition(condition Condition) *user {
|
func (u *user) List(ctx context.Context, opts ...QueryOption) (users []*User, err error) {
|
||||||
u.condition = condition
|
options := new(queryOpts)
|
||||||
return u
|
for _, opt := range opts {
|
||||||
}
|
opt(options)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *user) Get(ctx context.Context) (*User, error) {
|
u.builder.WriteString(queryUserStmt)
|
||||||
u.builder.WriteString(userQuery)
|
options.writeCondition(&u.builder)
|
||||||
u.writeCondition()
|
options.writeOrderBy(&u.builder)
|
||||||
return scanUser(u.client.QueryRow(ctx, u.builder.String(), u.builder.args...))
|
options.writeLimit(&u.builder)
|
||||||
}
|
options.writeOffset(&u.builder)
|
||||||
|
|
||||||
func (u *user) List(ctx context.Context) (users []*User, err error) {
|
|
||||||
u.builder.WriteString(userQuery)
|
|
||||||
u.writeCondition()
|
|
||||||
|
|
||||||
rows, err := u.client.Query(ctx, u.builder.String(), u.builder.args...)
|
rows, err := u.client.Query(ctx, u.builder.String(), u.builder.args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,7 +80,23 @@ func (u *user) List(ctx context.Context) (users []*User, err error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *user) Get(ctx context.Context, opts ...QueryOption) (*User, error) {
|
||||||
|
options := new(queryOpts)
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.builder.WriteString(queryUserStmt)
|
||||||
|
options.writeCondition(&u.builder)
|
||||||
|
options.writeOrderBy(&u.builder)
|
||||||
|
options.writeLimit(&u.builder)
|
||||||
|
options.writeOffset(&u.builder)
|
||||||
|
|
||||||
|
return scanUser(u.client.QueryRow(ctx, u.builder.String(), u.builder.args...))
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TODO: change to separate statements and tables
|
||||||
createUserCte = `WITH user AS (` +
|
createUserCte = `WITH user AS (` +
|
||||||
`INSERT INTO users (instance_id, org_id, id, username, type) VALUES ($1, $2, $3, $4, $5)` +
|
`INSERT INTO users (instance_id, org_id, id, username, type) VALUES ($1, $2, $3, $4, $5)` +
|
||||||
` RETURNING *)`
|
` RETURNING *)`
|
||||||
@@ -111,11 +120,24 @@ func (u *user) Create(ctx context.Context, user *User) error {
|
|||||||
u.builder.WriteString(createMachineStmt)
|
u.builder.WriteString(createMachineStmt)
|
||||||
u.builder.appendArgs(trait.Description)
|
u.builder.appendArgs(trait.Description)
|
||||||
}
|
}
|
||||||
return u.client.QueryRow(ctx, u.builder.String(), u.builder.args...).Scan(user.CreatedAt, user.UpdatedAt)
|
return u.client.QueryRow(ctx, u.builder.String(), u.builder.args...).Scan(&user.Dates.CreatedAt, &user.Dates.UpdatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *user) Update(ctx context.Context, condition Condition, changes ...Change) error {
|
||||||
|
u.builder.WriteString("UPDATE users SET ")
|
||||||
|
Changes(changes).writeTo(&u.builder)
|
||||||
|
u.writeCondition(condition)
|
||||||
|
return u.client.Exec(ctx, u.builder.String(), u.builder.args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *user) Delete(ctx context.Context, condition Condition) error {
|
||||||
|
u.builder.WriteString("DELETE FROM users")
|
||||||
|
u.writeCondition(condition)
|
||||||
|
return u.client.Exec(ctx, u.builder.String(), u.builder.args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) InstanceIDColumn() Column {
|
func (u *user) InstanceIDColumn() Column {
|
||||||
return column{name: "u.instance_id"}
|
return column{name: "instance_id"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) InstanceIDCondition(instanceID string) Condition {
|
func (u *user) InstanceIDCondition(instanceID string) Condition {
|
||||||
@@ -123,7 +145,7 @@ func (u *user) InstanceIDCondition(instanceID string) Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) OrgIDColumn() Column {
|
func (u *user) OrgIDColumn() Column {
|
||||||
return column{name: "u.org_id"}
|
return column{name: "org_id"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) OrgIDCondition(orgID string) Condition {
|
func (u *user) OrgIDCondition(orgID string) Condition {
|
||||||
@@ -131,7 +153,7 @@ func (u *user) OrgIDCondition(orgID string) Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) IDColumn() Column {
|
func (u *user) IDColumn() Column {
|
||||||
return column{name: "u.id"}
|
return column{name: "id"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) IDCondition(userID string) Condition {
|
func (u *user) IDCondition(userID string) Condition {
|
||||||
@@ -140,7 +162,7 @@ func (u *user) IDCondition(userID string) Condition {
|
|||||||
|
|
||||||
func (u *user) UsernameColumn() Column {
|
func (u *user) UsernameColumn() Column {
|
||||||
return ignoreCaseCol{
|
return ignoreCaseCol{
|
||||||
column: column{name: "u.username"},
|
column: column{name: "username"},
|
||||||
suffix: "_lower",
|
suffix: "_lower",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +176,7 @@ func (u *user) UsernameCondition(op TextOperator, username string) Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) CreatedAtColumn() Column {
|
func (u *user) CreatedAtColumn() Column {
|
||||||
return column{name: "u.created_at"}
|
return column{name: "created_at"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) CreatedAtCondition(op NumberOperator, createdAt time.Time) Condition {
|
func (u *user) CreatedAtCondition(op NumberOperator, createdAt time.Time) Condition {
|
||||||
@@ -162,7 +184,7 @@ func (u *user) CreatedAtCondition(op NumberOperator, createdAt time.Time) Condit
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) UpdatedAtColumn() Column {
|
func (u *user) UpdatedAtColumn() Column {
|
||||||
return column{name: "u.updated_at"}
|
return column{name: "updated_at"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) UpdatedAtCondition(op NumberOperator, updatedAt time.Time) Condition {
|
func (u *user) UpdatedAtCondition(op NumberOperator, updatedAt time.Time) Condition {
|
||||||
@@ -170,7 +192,7 @@ func (u *user) UpdatedAtCondition(op NumberOperator, updatedAt time.Time) Condit
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) DeletedAtColumn() Column {
|
func (u *user) DeletedAtColumn() Column {
|
||||||
return column{name: "u.deleted_at"}
|
return column{name: "deleted_at"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) DeletedCondition(isDeleted bool) Condition {
|
func (u *user) DeletedCondition(isDeleted bool) Condition {
|
||||||
@@ -184,12 +206,24 @@ func (u *user) DeletedAtCondition(op NumberOperator, deletedAt time.Time) Condit
|
|||||||
return newNumberCondition(u.DeletedAtColumn(), op, deletedAt)
|
return newNumberCondition(u.DeletedAtColumn(), op, deletedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) writeCondition() {
|
func (u *user) writeCondition(condition Condition) {
|
||||||
if u.condition == nil {
|
if condition == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u.builder.WriteString(" WHERE ")
|
u.builder.WriteString(" WHERE ")
|
||||||
u.condition.writeTo(&u.builder)
|
condition.writeTo(&u.builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u user) columns() Columns {
|
||||||
|
return Columns{
|
||||||
|
u.InstanceIDColumn(),
|
||||||
|
u.OrgIDColumn(),
|
||||||
|
u.IDColumn(),
|
||||||
|
u.UsernameColumn(),
|
||||||
|
u.CreatedAtColumn(),
|
||||||
|
u.UpdatedAtColumn(),
|
||||||
|
u.DeletedAtColumn(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanUser(scanner database.Scanner) (*User, error) {
|
func scanUser(scanner database.Scanner) (*User, error) {
|
||||||
|
@@ -46,11 +46,11 @@ func (u *user) Human() *userHuman {
|
|||||||
|
|
||||||
const userEmailQuery = `SELECT h.email_address, h.email_verified_at FROM user_humans h`
|
const userEmailQuery = `SELECT h.email_address, h.email_verified_at FROM user_humans h`
|
||||||
|
|
||||||
func (u *userHuman) GetEmail(ctx context.Context) (*Email, error) {
|
func (u *userHuman) GetEmail(ctx context.Context, condition Condition) (*Email, error) {
|
||||||
var email Email
|
var email Email
|
||||||
|
|
||||||
u.builder.WriteString(userEmailQuery)
|
u.builder.WriteString(userEmailQuery)
|
||||||
u.writeCondition()
|
u.writeCondition(condition)
|
||||||
|
|
||||||
err := u.client.QueryRow(ctx, u.builder.String(), u.builder.args...).Scan(
|
err := u.client.QueryRow(ctx, u.builder.String(), u.builder.args...).Scan(
|
||||||
&email.Address,
|
&email.Address,
|
||||||
@@ -63,10 +63,10 @@ func (u *userHuman) GetEmail(ctx context.Context) (*Email, error) {
|
|||||||
return &email, nil
|
return &email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) Update(ctx context.Context, changes ...Change) error {
|
func (h userHuman) Update(ctx context.Context, condition Condition, changes ...Change) error {
|
||||||
h.builder.WriteString(`UPDATE human_users h SET `)
|
h.builder.WriteString(`UPDATE human_users SET `)
|
||||||
Changes(changes).writeTo(&h.builder)
|
Changes(changes).writeTo(&h.builder)
|
||||||
h.writeCondition()
|
h.writeCondition(condition)
|
||||||
|
|
||||||
stmt := h.builder.String()
|
stmt := h.builder.String()
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ func (h userHuman) SetFirstName(firstName string) Change {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) FirstNameColumn() Column {
|
func (h userHuman) FirstNameColumn() Column {
|
||||||
return column{"h.first_name"}
|
return column{"first_name"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) FirstNameCondition(op TextOperator, firstName string) Condition {
|
func (h userHuman) FirstNameCondition(op TextOperator, firstName string) Condition {
|
||||||
@@ -90,7 +90,7 @@ func (h userHuman) SetLastName(lastName string) Change {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) LastNameColumn() Column {
|
func (h userHuman) LastNameColumn() Column {
|
||||||
return column{"h.last_name"}
|
return column{"last_name"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) LastNameCondition(op TextOperator, lastName string) Condition {
|
func (h userHuman) LastNameCondition(op TextOperator, lastName string) Condition {
|
||||||
@@ -99,7 +99,7 @@ func (h userHuman) LastNameCondition(op TextOperator, lastName string) Condition
|
|||||||
|
|
||||||
func (h userHuman) EmailAddressColumn() Column {
|
func (h userHuman) EmailAddressColumn() Column {
|
||||||
return ignoreCaseCol{
|
return ignoreCaseCol{
|
||||||
column: column{"h.email_address"},
|
column: column{"email_address"},
|
||||||
suffix: "_lower",
|
suffix: "_lower",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ func (h userHuman) EmailAddressCondition(op TextOperator, email string) Conditio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) EmailVerifiedAtColumn() Column {
|
func (h userHuman) EmailVerifiedAtColumn() Column {
|
||||||
return column{"h.email_verified_at"}
|
return column{"email_verified_at"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *userHuman) EmailAddressVerifiedCondition(isVerified bool) Condition {
|
func (h *userHuman) EmailAddressVerifiedCondition(isVerified bool) Condition {
|
||||||
@@ -144,7 +144,7 @@ func (h userHuman) SetEmail(address string, verified *time.Time) Change {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) PhoneNumberColumn() Column {
|
func (h userHuman) PhoneNumberColumn() Column {
|
||||||
return column{"h.phone_number"}
|
return column{"phone_number"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) SetPhoneNumber(number string) Change {
|
func (h userHuman) SetPhoneNumber(number string) Change {
|
||||||
@@ -156,7 +156,7 @@ func (h userHuman) PhoneNumberCondition(op TextOperator, phoneNumber string) Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) PhoneVerifiedAtColumn() Column {
|
func (h userHuman) PhoneVerifiedAtColumn() Column {
|
||||||
return column{"h.phone_verified_at"}
|
return column{"phone_verified_at"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) PhoneNumberVerifiedCondition(isVerified bool) Condition {
|
func (h userHuman) PhoneNumberVerifiedCondition(isVerified bool) Condition {
|
||||||
@@ -185,3 +185,19 @@ func (h userHuman) SetPhone(number string, verifiedAt *time.Time) Change {
|
|||||||
newUpdatePtrColumn(h.PhoneVerifiedAtColumn(), verifiedAt),
|
newUpdatePtrColumn(h.PhoneVerifiedAtColumn(), verifiedAt),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h userHuman) columns() Columns {
|
||||||
|
return append(h.user.columns(),
|
||||||
|
h.FirstNameColumn(),
|
||||||
|
h.LastNameColumn(),
|
||||||
|
h.EmailAddressColumn(),
|
||||||
|
h.EmailVerifiedAtColumn(),
|
||||||
|
h.PhoneNumberColumn(),
|
||||||
|
h.PhoneVerifiedAtColumn(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h userHuman) writeReturning(builder *statementBuilder) {
|
||||||
|
builder.WriteString(" RETURNING ")
|
||||||
|
h.columns().writeTo(builder)
|
||||||
|
}
|
||||||
|
@@ -24,12 +24,34 @@ func (u *user) Machine() *userMachine {
|
|||||||
return &userMachine{user: u}
|
return &userMachine{user: u}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m userMachine) Update(ctx context.Context, cols ...Change) (*Machine, error) {
|
func (m userMachine) Update(ctx context.Context, condition Condition, changes ...Change) ([]*Machine, error) {
|
||||||
return nil, nil
|
m.builder.WriteString("UPDATE user_machines SET ")
|
||||||
|
Changes(changes).writeTo(&m.builder)
|
||||||
|
m.writeCondition(condition)
|
||||||
|
m.writeReturning()
|
||||||
|
|
||||||
|
var machines []*Machine
|
||||||
|
rows, err := m.client.Query(ctx, m.builder.String(), m.builder.args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
machine := new(Machine)
|
||||||
|
if err := rows.Scan(&machine.Description); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
machines = append(machines, machine)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return machines, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (userMachine) DescriptionColumn() Column {
|
func (userMachine) DescriptionColumn() Column {
|
||||||
return column{"m.description"}
|
return column{"description"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m userMachine) SetDescription(description string) Change {
|
func (m userMachine) SetDescription(description string) Change {
|
||||||
@@ -39,3 +61,12 @@ func (m userMachine) SetDescription(description string) Change {
|
|||||||
func (m userMachine) DescriptionCondition(op TextOperator, description string) Condition {
|
func (m userMachine) DescriptionCondition(op TextOperator, description string) Condition {
|
||||||
return newTextCondition(m.DescriptionColumn(), op, description)
|
return newTextCondition(m.DescriptionColumn(), op, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m userMachine) columns() Columns {
|
||||||
|
return append(m.user.columns(), m.DescriptionColumn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *userMachine) writeReturning() {
|
||||||
|
m.builder.WriteString(" RETURNING ")
|
||||||
|
m.columns().writeTo(&m.builder)
|
||||||
|
}
|
||||||
|
@@ -11,32 +11,38 @@ import (
|
|||||||
func TestQueryUser(t *testing.T) {
|
func TestQueryUser(t *testing.T) {
|
||||||
t.Run("User filters", func(t *testing.T) {
|
t.Run("User filters", func(t *testing.T) {
|
||||||
user := v4.UserRepository(nil)
|
user := v4.UserRepository(nil)
|
||||||
user.WithCondition(
|
u, err := user.Get(context.Background(),
|
||||||
v4.And(
|
v4.WithCondition(
|
||||||
v4.Or(
|
v4.And(
|
||||||
user.IDCondition("test"),
|
v4.Or(
|
||||||
user.IDCondition("2"),
|
user.IDCondition("test"),
|
||||||
|
user.IDCondition("2"),
|
||||||
|
),
|
||||||
|
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||||
),
|
),
|
||||||
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
|
||||||
),
|
),
|
||||||
).Get(context.Background())
|
v4.WithOrderBy(user.CreatedAtColumn()),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, u)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("machine and human filters", func(t *testing.T) {
|
t.Run("machine and human filters", func(t *testing.T) {
|
||||||
user := v4.UserRepository(nil)
|
user := v4.UserRepository(nil)
|
||||||
machine := user.Machine()
|
machine := user.Machine()
|
||||||
human := user.Human()
|
human := user.Human()
|
||||||
user.WithCondition(
|
email, err := human.GetEmail(context.Background(), v4.And(
|
||||||
v4.And(
|
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||||
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
v4.Or(
|
||||||
v4.Or(
|
machine.DescriptionCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||||
machine.DescriptionCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
human.EmailAddressVerifiedCondition(true),
|
||||||
human.EmailAddressVerifiedCondition(true),
|
v4.IsNotNull(machine.DescriptionColumn()),
|
||||||
v4.IsNotNull(machine.DescriptionColumn()),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
human.GetEmail(context.Background())
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, email)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +62,6 @@ func TestArg(t *testing.T) {
|
|||||||
func TestWriteUser(t *testing.T) {
|
func TestWriteUser(t *testing.T) {
|
||||||
t.Run("update user", func(t *testing.T) {
|
t.Run("update user", func(t *testing.T) {
|
||||||
user := v4.UserRepository(nil)
|
user := v4.UserRepository(nil)
|
||||||
user.WithCondition(user.IDCondition("test")).Human().Update(
|
user.Update(context.Background(), user.IDCondition("test"), user.SetUsername("test"))
|
||||||
context.Background(),
|
|
||||||
user.SetUsername("test"),
|
|
||||||
)
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user