Files
zitadel/backend/v3/storage/database/repository/user_human.go

209 lines
6.9 KiB
Go
Raw Normal View History

2025-05-08 07:42:53 +02:00
package repository
2025-04-29 06:03:47 +02:00
import (
"context"
"time"
2025-05-06 07:18:11 +02:00
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database"
)
2025-04-29 06:03:47 +02:00
2025-05-06 07:18:11 +02:00
// -------------------------------------------------------------
// repository
// -------------------------------------------------------------
2025-04-29 06:03:47 +02:00
type userHuman struct {
*user
}
2025-05-06 07:18:11 +02:00
var _ domain.HumanRepository = (*userHuman)(nil)
2025-04-29 06:03:47 +02:00
const userEmailQuery = `SELECT h.email_address, h.email_verified_at FROM user_humans h`
2025-05-06 07:18:11 +02:00
// GetEmail implements [domain.HumanRepository].
func (u *userHuman) GetEmail(ctx context.Context, condition database.Condition) (*domain.Email, error) {
var email domain.Email
2025-04-29 06:03:47 +02:00
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
builder := database.StatementBuilder{}
builder.WriteString(userEmailQuery)
u.writeCondition(builder, condition)
2025-04-29 06:03:47 +02:00
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
err := u.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(
2025-04-29 06:03:47 +02:00
&email.Address,
2025-05-06 07:18:11 +02:00
&email.VerifiedAt,
2025-04-29 06:03:47 +02:00
)
if err != nil {
return nil, err
}
return &email, nil
}
2025-05-06 07:18:11 +02:00
// Update implements [domain.HumanRepository].
func (h userHuman) Update(ctx context.Context, condition database.Condition, changes ...database.Change) error {
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
builder := database.StatementBuilder{}
builder.WriteString(`UPDATE human_users SET `)
database.Changes(changes).Write(&builder)
h.writeCondition(builder, condition)
2025-04-29 06:03:47 +02:00
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
stmt := builder.String()
2025-04-29 06:03:47 +02:00
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
_, err := h.client.Exec(ctx, stmt, builder.Args()...)
return err
2025-05-06 07:18:11 +02:00
}
// -------------------------------------------------------------
// changes
// -------------------------------------------------------------
// SetFirstName implements [domain.humanChanges].
func (h userHuman) SetFirstName(firstName string) database.Change {
return database.NewChange(h.FirstNameColumn(), firstName)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetLastName implements [domain.humanChanges].
func (h userHuman) SetLastName(lastName string) database.Change {
return database.NewChange(h.LastNameColumn(), lastName)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetEmail implements [domain.humanChanges].
func (h userHuman) SetEmail(address string, verified *time.Time) database.Change {
return database.NewChanges(
h.SetEmailAddress(address),
database.NewChangePtr(h.EmailVerifiedAtColumn(), verified),
)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetEmailAddress implements [domain.humanChanges].
func (h userHuman) SetEmailAddress(address string) database.Change {
return database.NewChange(h.EmailAddressColumn(), address)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetEmailVerifiedAt implements [domain.humanChanges].
func (h userHuman) SetEmailVerifiedAt(at time.Time) database.Change {
if at.IsZero() {
return database.NewChange(h.EmailVerifiedAtColumn(), database.NowInstruction)
}
return database.NewChange(h.EmailVerifiedAtColumn(), at)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetPhone implements [domain.humanChanges].
func (h userHuman) SetPhone(number string, verifiedAt *time.Time) database.Change {
return database.NewChanges(
h.SetPhoneNumber(number),
database.NewChangePtr(h.PhoneVerifiedAtColumn(), verifiedAt),
)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetPhoneNumber implements [domain.humanChanges].
func (h userHuman) SetPhoneNumber(number string) database.Change {
return database.NewChange(h.PhoneNumberColumn(), number)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// SetPhoneVerifiedAt implements [domain.humanChanges].
func (h userHuman) SetPhoneVerifiedAt(at time.Time) database.Change {
if at.IsZero() {
return database.NewChange(h.PhoneVerifiedAtColumn(), database.NowInstruction)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
return database.NewChange(h.PhoneVerifiedAtColumn(), at)
}
// -------------------------------------------------------------
// conditions
// -------------------------------------------------------------
// FirstNameCondition implements [domain.humanConditions].
func (h userHuman) FirstNameCondition(op database.TextOperation, firstName string) database.Condition {
return database.NewTextCondition(h.FirstNameColumn(), op, firstName)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// LastNameCondition implements [domain.humanConditions].
func (h userHuman) LastNameCondition(op database.TextOperation, lastName string) database.Condition {
return database.NewTextCondition(h.LastNameColumn(), op, lastName)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// EmailAddressCondition implements [domain.humanConditions].
func (h userHuman) EmailAddressCondition(op database.TextOperation, email string) database.Condition {
return database.NewTextCondition(h.EmailAddressColumn(), op, email)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// EmailVerifiedCondition implements [domain.humanConditions].
func (h *userHuman) EmailVerifiedCondition(isVerified bool) database.Condition {
2025-04-29 06:03:47 +02:00
if isVerified {
2025-05-06 07:18:11 +02:00
return database.IsNotNull(h.EmailVerifiedAtColumn())
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
return database.IsNull(h.EmailVerifiedAtColumn())
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// EmailVerifiedAtCondition implements [domain.humanConditions].
func (h userHuman) EmailVerifiedAtCondition(op database.NumberOperation, verifiedAt time.Time) database.Condition {
return database.NewNumberCondition(h.EmailVerifiedAtColumn(), op, verifiedAt)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// PhoneNumberCondition implements [domain.humanConditions].
func (h userHuman) PhoneNumberCondition(op database.TextOperation, phoneNumber string) database.Condition {
return database.NewTextCondition(h.PhoneNumberColumn(), op, phoneNumber)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// PhoneVerifiedCondition implements [domain.humanConditions].
func (h userHuman) PhoneVerifiedCondition(isVerified bool) database.Condition {
if isVerified {
return database.IsNotNull(h.PhoneVerifiedAtColumn())
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
return database.IsNull(h.PhoneVerifiedAtColumn())
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// PhoneVerifiedAtCondition implements [domain.humanConditions].
func (h userHuman) PhoneVerifiedAtCondition(op database.NumberOperation, verifiedAt time.Time) database.Condition {
return database.NewNumberCondition(h.PhoneVerifiedAtColumn(), op, verifiedAt)
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// -------------------------------------------------------------
// columns
// -------------------------------------------------------------
2025-04-29 06:03:47 +02:00
2025-05-06 07:18:11 +02:00
// FirstNameColumn implements [domain.humanColumns].
func (h userHuman) FirstNameColumn() database.Column {
return database.NewColumn("first_name")
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// LastNameColumn implements [domain.humanColumns].
func (h userHuman) LastNameColumn() database.Column {
return database.NewColumn("last_name")
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// EmailAddressColumn implements [domain.humanColumns].
func (h userHuman) EmailAddressColumn() database.Column {
return database.NewIgnoreCaseColumn("email_address", "_lower")
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// EmailVerifiedAtColumn implements [domain.humanColumns].
func (h userHuman) EmailVerifiedAtColumn() database.Column {
return database.NewColumn("email_verified_at")
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// PhoneNumberColumn implements [domain.humanColumns].
func (h userHuman) PhoneNumberColumn() database.Column {
return database.NewColumn("phone_number")
2025-04-29 06:03:47 +02:00
}
2025-05-06 07:18:11 +02:00
// PhoneVerifiedAtColumn implements [domain.humanColumns].
func (h userHuman) PhoneVerifiedAtColumn() database.Column {
return database.NewColumn("phone_verified_at")
2025-04-29 06:03:47 +02:00
}
2025-04-30 09:30:48 +02:00
// func (h userHuman) columns() database.Columns {
// return append(h.user.columns(),
// h.FirstNameColumn(),
// h.LastNameColumn(),
// h.EmailAddressColumn(),
// h.EmailVerifiedAtColumn(),
// h.PhoneNumberColumn(),
// h.PhoneVerifiedAtColumn(),
// )
// }
// func (h userHuman) writeReturning(builder *database.StatementBuilder) {
// builder.WriteString(" RETURNING ")
// h.columns().Write(builder)
// }