mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:07:30 +00:00
v4 without errors
This commit is contained in:
@@ -15,7 +15,7 @@ var (
|
||||
userCodeAlgorithm crypto.EncryptionAlgorithm
|
||||
tracer tracing.Tracer
|
||||
|
||||
// userRepo func(database.QueryExecutor) UserRepository
|
||||
userRepo func(database.QueryExecutor) UserRepository
|
||||
instanceRepo func(database.QueryExecutor) InstanceRepository
|
||||
cryptoRepo func(database.QueryExecutor) CryptoRepository
|
||||
orgRepo func(database.QueryExecutor) OrgRepository
|
||||
@@ -39,9 +39,9 @@ func SetTracer(t tracing.Tracer) {
|
||||
tracer = t
|
||||
}
|
||||
|
||||
// func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
||||
// userRepo = repo
|
||||
// }
|
||||
func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
||||
userRepo = repo
|
||||
}
|
||||
|
||||
func SetInstanceRepository(repo func(database.QueryExecutor) InstanceRepository) {
|
||||
instanceRepo = repo
|
||||
|
@@ -2,8 +2,7 @@ package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v4 "github.com/zitadel/zitadel/backend/v3/storage/database/repository/stmt/v4"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EmailVerifiedCommand struct {
|
||||
@@ -27,7 +26,8 @@ var (
|
||||
|
||||
// Execute implements [Commander]
|
||||
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]
|
||||
@@ -78,8 +78,9 @@ func (cmd *SendCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts)
|
||||
if cmd.Email != "" {
|
||||
return nil
|
||||
}
|
||||
email, err := userRepo(opts.DB).Human().ByID(cmd.UserID).Exec().GetEmail(ctx)
|
||||
if err != nil || email.IsVerified {
|
||||
repo := userRepo(opts.DB).Human()
|
||||
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||
if err != nil || !email.VerifiedAt.IsZero() {
|
||||
return err
|
||||
}
|
||||
cmd.Email = email.Address
|
||||
@@ -137,10 +138,9 @@ func (cmd *ReturnCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts
|
||||
if cmd.Email != "" {
|
||||
return nil
|
||||
}
|
||||
user := v4.UserRepository(opts.DB)
|
||||
user.WithCondition(user.IDCondition(cmd.UserID))
|
||||
email, err := user.he.GetEmail(ctx)
|
||||
if err != nil || email.IsVerified {
|
||||
repo := userRepo(opts.DB).Human()
|
||||
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||
if err != nil || !email.VerifiedAt.IsZero() {
|
||||
return err
|
||||
}
|
||||
cmd.Email = email.Address
|
||||
|
@@ -38,7 +38,8 @@ func (cmd *SetEmailCommand) Execute(ctx context.Context, opts *CommandOpts) erro
|
||||
}
|
||||
defer func() { err = close(ctx, err) }()
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@@ -59,6 +60,6 @@ func (cmd *SetEmailCommand) Events() []*eventstore.Event {
|
||||
}
|
||||
|
||||
// applyOnCreateHuman implements [CreateHumanOpt].
|
||||
func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand[Human]) {
|
||||
func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand) {
|
||||
createUserCmd.email = cmd
|
||||
}
|
||||
|
@@ -9,13 +9,13 @@ import (
|
||||
|
||||
type userColumns interface {
|
||||
// TODO: move v4.columns to domain
|
||||
InstanceIDColumn() column
|
||||
OrgIDColumn() column
|
||||
IDColumn() column
|
||||
usernameColumn() column
|
||||
CreatedAtColumn() column
|
||||
UpdatedAtColumn() column
|
||||
DeletedAtColumn() column
|
||||
InstanceIDColumn() v4.Column
|
||||
OrgIDColumn() v4.Column
|
||||
IDColumn() v4.Column
|
||||
usernameColumn() v4.Column
|
||||
CreatedAtColumn() v4.Column
|
||||
UpdatedAtColumn() v4.Column
|
||||
DeletedAtColumn() v4.Column
|
||||
}
|
||||
|
||||
type userConditions interface {
|
||||
@@ -29,30 +29,35 @@ type userConditions interface {
|
||||
DeletedAtCondition(op v4.NumberOperator, deletedAt time.Time) v4.Condition
|
||||
}
|
||||
|
||||
type userChanges interface {
|
||||
SetUsername(username string) v4.Change
|
||||
}
|
||||
|
||||
type UserRepository interface {
|
||||
userColumns
|
||||
userConditions
|
||||
userChanges
|
||||
// TODO: move condition to domain
|
||||
WithCondition(condition v4.Condition) UserRepository
|
||||
Get(ctx context.Context) (*User, error)
|
||||
List(ctx context.Context) ([]*User, error)
|
||||
Create(ctx context.Context, user *User) error
|
||||
Delete(ctx context.Context) error
|
||||
Get(ctx context.Context, opts v4.QueryOption) (*User, error)
|
||||
List(ctx context.Context, opts v4.QueryOption) ([]*User, error)
|
||||
Delete(ctx context.Context, condition v4.Condition) error
|
||||
|
||||
Human() HumanRepository
|
||||
Machine() MachineRepository
|
||||
}
|
||||
|
||||
type humanColumns interface {
|
||||
FirstNameColumn() column
|
||||
LastNameColumn() column
|
||||
EmailAddressColumn() column
|
||||
EmailVerifiedAtColumn() column
|
||||
PhoneNumberColumn() column
|
||||
PhoneVerifiedAtColumn() column
|
||||
userColumns
|
||||
FirstNameColumn() v4.Column
|
||||
LastNameColumn() v4.Column
|
||||
EmailAddressColumn() v4.Column
|
||||
EmailVerifiedAtColumn() v4.Column
|
||||
PhoneNumberColumn() v4.Column
|
||||
PhoneVerifiedAtColumn() v4.Column
|
||||
}
|
||||
|
||||
type humanConditions interface {
|
||||
userConditions
|
||||
FirstNameCondition(op v4.TextOperator, firstName string) v4.Condition
|
||||
LastNameCondition(op v4.TextOperator, lastName 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
|
||||
}
|
||||
|
||||
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 {
|
||||
humanColumns
|
||||
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
|
||||
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 {
|
||||
DescriptionColumn() column
|
||||
userColumns
|
||||
DescriptionColumn() v4.Column
|
||||
}
|
||||
|
||||
type machineConditions interface {
|
||||
userConditions
|
||||
DescriptionCondition(op v4.TextOperator, description string) v4.Condition
|
||||
}
|
||||
|
||||
type machineChanges interface {
|
||||
userChanges
|
||||
SetDescription(description string) v4.Change
|
||||
}
|
||||
|
||||
type MachineRepository interface {
|
||||
machineColumns
|
||||
machineConditions
|
||||
machineChanges
|
||||
|
||||
Create(ctx context.Context, user *User) error
|
||||
Update(ctx context.Context, condition v4.Condition, changes ...v4.Change) error
|
||||
}
|
||||
|
||||
// type UserRepository interface {
|
||||
@@ -171,6 +203,11 @@ type User struct {
|
||||
v4.User
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
v4.Email
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
// type userTraits interface {
|
||||
// isUserTraits()
|
||||
// }
|
||||
|
@@ -50,6 +50,17 @@ var _ Change = Changes(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 {
|
||||
writeTo(builder *statementBuilder)
|
||||
}
|
||||
|
@@ -59,6 +59,8 @@ CREATE TABLE users (
|
||||
-- , CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||
) INHERITS (org_objects);
|
||||
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
|
||||
CREATE TRIGGER set_updated_at
|
||||
BEFORE UPDATE
|
||||
ON users
|
||||
@@ -74,6 +76,8 @@ CREATE TABLE human_users(
|
||||
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||
) INHERITS (users);
|
||||
|
||||
CREATE INDEX idx_human_users_username ON human_users(username);
|
||||
|
||||
CREATE TRIGGER set_updated_at
|
||||
BEFORE UPDATE
|
||||
ON human_users
|
||||
@@ -88,23 +92,15 @@ CREATE TABLE machine_users(
|
||||
, CONSTRAINT fk_instances FOREIGN KEY (instance_id) REFERENCES instances(id)
|
||||
) INHERITS (users);
|
||||
|
||||
CREATE INDEX idx_machine_users_username ON machine_users(username);
|
||||
|
||||
CREATE TRIGGER set_updated_at
|
||||
BEFORE UPDATE
|
||||
ON machine_users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
|
||||
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 (
|
||||
CREATE VIEW users_view AS (
|
||||
SELECT
|
||||
id
|
||||
, created_at
|
||||
@@ -113,27 +109,16 @@ SELECT
|
||||
, instance_id
|
||||
, org_id
|
||||
, username
|
||||
, first_name
|
||||
, last_name
|
||||
, description
|
||||
FROM (
|
||||
(SELECT
|
||||
id
|
||||
, created_at
|
||||
, updated_at
|
||||
, deleted_at
|
||||
, instance_id
|
||||
, org_id
|
||||
, username
|
||||
, tableoid::regclass::TEXT AS type
|
||||
, first_name
|
||||
, last_name
|
||||
, NULL AS description
|
||||
FROM
|
||||
human_users)
|
||||
human_users
|
||||
|
||||
UNION
|
||||
|
||||
(SELECT
|
||||
SELECT
|
||||
id
|
||||
, created_at
|
||||
, updated_at
|
||||
@@ -141,9 +126,10 @@ UNION
|
||||
, instance_id
|
||||
, org_id
|
||||
, username
|
||||
, tableoid::regclass::TEXT AS type
|
||||
, NULL AS first_name
|
||||
, NULL AS last_name
|
||||
, description
|
||||
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
|
||||
}
|
||||
|
||||
const userQuery = `SELECT u.instance_id, u.org_id, u.id, u.username, u.type, u.created_at, u.updated_at, u.deleted_at,` +
|
||||
` h.first_name, h.last_name, h.email_address, h.email_verified_at, h.phone_number, h.phone_verified_at, m.description` +
|
||||
` FROM users u` +
|
||||
` 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`
|
||||
const queryUserStmt = `SELECT instance_id, org_id, id, username, type, created_at, updated_at, deleted_at,` +
|
||||
` first_name, last_name, email_address, email_verified_at, phone_number, phone_verified_at, description` +
|
||||
` FROM users_view`
|
||||
|
||||
type user struct {
|
||||
builder statementBuilder
|
||||
client database.QueryExecutor
|
||||
|
||||
condition Condition
|
||||
}
|
||||
|
||||
func UserRepository(client database.QueryExecutor) *user {
|
||||
@@ -48,20 +44,17 @@ func UserRepository(client database.QueryExecutor) *user {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *user) WithCondition(condition Condition) *user {
|
||||
u.condition = condition
|
||||
return u
|
||||
}
|
||||
func (u *user) List(ctx context.Context, opts ...QueryOption) (users []*User, err error) {
|
||||
options := new(queryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
func (u *user) Get(ctx context.Context) (*User, error) {
|
||||
u.builder.WriteString(userQuery)
|
||||
u.writeCondition()
|
||||
return scanUser(u.client.QueryRow(ctx, u.builder.String(), u.builder.args...))
|
||||
}
|
||||
|
||||
func (u *user) List(ctx context.Context) (users []*User, err error) {
|
||||
u.builder.WriteString(userQuery)
|
||||
u.writeCondition()
|
||||
u.builder.WriteString(queryUserStmt)
|
||||
options.writeCondition(&u.builder)
|
||||
options.writeOrderBy(&u.builder)
|
||||
options.writeLimit(&u.builder)
|
||||
options.writeOffset(&u.builder)
|
||||
|
||||
rows, err := u.client.Query(ctx, u.builder.String(), u.builder.args...)
|
||||
if err != nil {
|
||||
@@ -87,7 +80,23 @@ func (u *user) List(ctx context.Context) (users []*User, err error) {
|
||||
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 (
|
||||
// TODO: change to separate statements and tables
|
||||
createUserCte = `WITH user AS (` +
|
||||
`INSERT INTO users (instance_id, org_id, id, username, type) VALUES ($1, $2, $3, $4, $5)` +
|
||||
` RETURNING *)`
|
||||
@@ -111,11 +120,24 @@ func (u *user) Create(ctx context.Context, user *User) error {
|
||||
u.builder.WriteString(createMachineStmt)
|
||||
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 {
|
||||
return column{name: "u.instance_id"}
|
||||
return column{name: "instance_id"}
|
||||
}
|
||||
|
||||
func (u *user) InstanceIDCondition(instanceID string) Condition {
|
||||
@@ -123,7 +145,7 @@ func (u *user) InstanceIDCondition(instanceID string) Condition {
|
||||
}
|
||||
|
||||
func (u *user) OrgIDColumn() Column {
|
||||
return column{name: "u.org_id"}
|
||||
return column{name: "org_id"}
|
||||
}
|
||||
|
||||
func (u *user) OrgIDCondition(orgID string) Condition {
|
||||
@@ -131,7 +153,7 @@ func (u *user) OrgIDCondition(orgID string) Condition {
|
||||
}
|
||||
|
||||
func (u *user) IDColumn() Column {
|
||||
return column{name: "u.id"}
|
||||
return column{name: "id"}
|
||||
}
|
||||
|
||||
func (u *user) IDCondition(userID string) Condition {
|
||||
@@ -140,7 +162,7 @@ func (u *user) IDCondition(userID string) Condition {
|
||||
|
||||
func (u *user) UsernameColumn() Column {
|
||||
return ignoreCaseCol{
|
||||
column: column{name: "u.username"},
|
||||
column: column{name: "username"},
|
||||
suffix: "_lower",
|
||||
}
|
||||
}
|
||||
@@ -154,7 +176,7 @@ func (u *user) UsernameCondition(op TextOperator, username string) Condition {
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -162,7 +184,7 @@ func (u *user) CreatedAtCondition(op NumberOperator, createdAt time.Time) Condit
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -170,7 +192,7 @@ func (u *user) UpdatedAtCondition(op NumberOperator, updatedAt time.Time) Condit
|
||||
}
|
||||
|
||||
func (u *user) DeletedAtColumn() Column {
|
||||
return column{name: "u.deleted_at"}
|
||||
return column{name: "deleted_at"}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (u *user) writeCondition() {
|
||||
if u.condition == nil {
|
||||
func (u *user) writeCondition(condition Condition) {
|
||||
if condition == nil {
|
||||
return
|
||||
}
|
||||
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) {
|
||||
|
@@ -46,11 +46,11 @@ func (u *user) Human() *userHuman {
|
||||
|
||||
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
|
||||
|
||||
u.builder.WriteString(userEmailQuery)
|
||||
u.writeCondition()
|
||||
u.writeCondition(condition)
|
||||
|
||||
err := u.client.QueryRow(ctx, u.builder.String(), u.builder.args...).Scan(
|
||||
&email.Address,
|
||||
@@ -63,10 +63,10 @@ func (u *userHuman) GetEmail(ctx context.Context) (*Email, error) {
|
||||
return &email, nil
|
||||
}
|
||||
|
||||
func (h userHuman) Update(ctx context.Context, changes ...Change) error {
|
||||
h.builder.WriteString(`UPDATE human_users h SET `)
|
||||
func (h userHuman) Update(ctx context.Context, condition Condition, changes ...Change) error {
|
||||
h.builder.WriteString(`UPDATE human_users SET `)
|
||||
Changes(changes).writeTo(&h.builder)
|
||||
h.writeCondition()
|
||||
h.writeCondition(condition)
|
||||
|
||||
stmt := h.builder.String()
|
||||
|
||||
@@ -78,7 +78,7 @@ func (h userHuman) SetFirstName(firstName string) Change {
|
||||
}
|
||||
|
||||
func (h userHuman) FirstNameColumn() Column {
|
||||
return column{"h.first_name"}
|
||||
return column{"first_name"}
|
||||
}
|
||||
|
||||
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 {
|
||||
return column{"h.last_name"}
|
||||
return column{"last_name"}
|
||||
}
|
||||
|
||||
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 {
|
||||
return ignoreCaseCol{
|
||||
column: column{"h.email_address"},
|
||||
column: column{"email_address"},
|
||||
suffix: "_lower",
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (h userHuman) EmailAddressCondition(op TextOperator, email string) Conditio
|
||||
}
|
||||
|
||||
func (h userHuman) EmailVerifiedAtColumn() Column {
|
||||
return column{"h.email_verified_at"}
|
||||
return column{"email_verified_at"}
|
||||
}
|
||||
|
||||
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 {
|
||||
return column{"h.phone_number"}
|
||||
return column{"phone_number"}
|
||||
}
|
||||
|
||||
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 {
|
||||
return column{"h.phone_verified_at"}
|
||||
return column{"phone_verified_at"}
|
||||
}
|
||||
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
func (m userMachine) Update(ctx context.Context, cols ...Change) (*Machine, error) {
|
||||
return nil, nil
|
||||
func (m userMachine) Update(ctx context.Context, condition Condition, changes ...Change) ([]*Machine, error) {
|
||||
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 {
|
||||
return column{"m.description"}
|
||||
return column{"description"}
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
t.Run("User filters", func(t *testing.T) {
|
||||
user := v4.UserRepository(nil)
|
||||
user.WithCondition(
|
||||
v4.And(
|
||||
v4.Or(
|
||||
user.IDCondition("test"),
|
||||
user.IDCondition("2"),
|
||||
u, err := user.Get(context.Background(),
|
||||
v4.WithCondition(
|
||||
v4.And(
|
||||
v4.Or(
|
||||
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) {
|
||||
user := v4.UserRepository(nil)
|
||||
machine := user.Machine()
|
||||
human := user.Human()
|
||||
user.WithCondition(
|
||||
v4.And(
|
||||
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||
v4.Or(
|
||||
machine.DescriptionCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||
human.EmailAddressVerifiedCondition(true),
|
||||
v4.IsNotNull(machine.DescriptionColumn()),
|
||||
),
|
||||
email, err := human.GetEmail(context.Background(), v4.And(
|
||||
user.UsernameCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||
v4.Or(
|
||||
machine.DescriptionCondition(v4.TextOperatorStartsWithIgnoreCase, "test"),
|
||||
human.EmailAddressVerifiedCondition(true),
|
||||
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) {
|
||||
t.Run("update user", func(t *testing.T) {
|
||||
user := v4.UserRepository(nil)
|
||||
user.WithCondition(user.IDCondition("test")).Human().Update(
|
||||
context.Background(),
|
||||
user.SetUsername("test"),
|
||||
)
|
||||
|
||||
user.Update(context.Background(), user.IDCondition("test"), user.SetUsername("test"))
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user