get / list implemented

This commit is contained in:
adlerhurst
2025-07-17 15:32:50 +02:00
parent 65cd4ec668
commit bb2d0aff3f
8 changed files with 168 additions and 53 deletions

View File

@@ -52,6 +52,14 @@ type InstanceDomainRepository interface {
instanceDomainConditions instanceDomainConditions
instanceDomainChanges instanceDomainChanges
// Get returns a single domain based on the criteria.
// If no domain is found, it returns an error of type [database.ErrNotFound].
// If multiple domains are found, it returns an error of type [database.ErrMultipleRows].
Get(ctx context.Context, opts ...database.QueryOption) (*InstanceDomain, error)
// List returns a list of domains based on the criteria.
// If no domains are found, it returns an empty slice.
List(ctx context.Context, opts ...database.QueryOption) ([]*InstanceDomain, error)
// Add adds a new domain to the instance. // Add adds a new domain to the instance.
Add(ctx context.Context, domain *AddInstanceDomain) error Add(ctx context.Context, domain *AddInstanceDomain) error
// Update updates an existing domain in the instance. // Update updates an existing domain in the instance.

View File

@@ -94,9 +94,3 @@ type MemberRepository interface {
RemoveMember(ctx context.Context, orgID, userID string) error RemoveMember(ctx context.Context, orgID, userID string) error
} }
// DomainRepository is a sub repository of the org repository and maybe the instance repository.
type DomainRepository interface {
AddDomain(ctx context.Context, domain string) error
SetDomainVerified(ctx context.Context, domain string) error
RemoveDomain(ctx context.Context, domain string) error
}

View File

@@ -56,6 +56,14 @@ type OrganizationDomainRepository interface {
organizationDomainConditions organizationDomainConditions
organizationDomainChanges organizationDomainChanges
// Get returns a single domain based on the criteria.
// If no domain is found, it returns an error of type [database.ErrNotFound].
// If multiple domains are found, it returns an error of type [database.ErrMultipleRows].
Get(ctx context.Context, opts ...database.QueryOption) (*OrganizationDomain, error)
// List returns a list of domains based on the criteria.
// If no domains are found, it returns an empty slice.
List(ctx context.Context, opts ...database.QueryOption) ([]*OrganizationDomain, error)
// Add adds a new domain to the organization. // Add adds a new domain to the organization.
Add(ctx context.Context, domain *AddOrganizationDomain) error Add(ctx context.Context, domain *AddOrganizationDomain) error
// Update updates an existing domain in the organization. // Update updates an existing domain in the organization.

View File

@@ -2,37 +2,58 @@ package database
type QueryOption func(opts *QueryOpts) type QueryOption func(opts *QueryOpts)
// WithCondition sets the condition for the query.
func WithCondition(condition Condition) QueryOption { func WithCondition(condition Condition) QueryOption {
return func(opts *QueryOpts) { return func(opts *QueryOpts) {
opts.Condition = condition opts.Condition = condition
} }
} }
// WithOrderBy sets the columns to order the results by.
func WithOrderBy(orderBy ...Column) QueryOption { func WithOrderBy(orderBy ...Column) QueryOption {
return func(opts *QueryOpts) { return func(opts *QueryOpts) {
opts.OrderBy = orderBy opts.OrderBy = orderBy
} }
} }
// WithLimit sets the maximum number of results to return.
func WithLimit(limit uint32) QueryOption { func WithLimit(limit uint32) QueryOption {
return func(opts *QueryOpts) { return func(opts *QueryOpts) {
opts.Limit = limit opts.Limit = limit
} }
} }
// WithOffset sets the number of results to skip before returning the results.
func WithOffset(offset uint32) QueryOption { func WithOffset(offset uint32) QueryOption {
return func(opts *QueryOpts) { return func(opts *QueryOpts) {
opts.Offset = offset opts.Offset = offset
} }
} }
// QueryOpts holds the options for a query.
// It is used to build the SQL SELECT statement.
type QueryOpts struct { type QueryOpts struct {
// Condition is the condition to filter the results.
// It is used to build the WHERE clause of the SQL statement.
Condition Condition Condition Condition
// OrderBy is the columns to order the results by.
// It is used to build the ORDER BY clause of the SQL statement.
OrderBy Columns OrderBy Columns
// Limit is the maximum number of results to return.
// It is used to build the LIMIT clause of the SQL statement.
Limit uint32 Limit uint32
// Offset is the number of results to skip before returning the results.
// It is used to build the OFFSET clause of the SQL statement.
Offset uint32 Offset uint32
} }
func (opts *QueryOpts) Write(builder *StatementBuilder) {
opts.WriteCondition(builder)
opts.WriteOrderBy(builder)
opts.WriteLimit(builder)
opts.WriteOffset(builder)
}
func (opts *QueryOpts) WriteCondition(builder *StatementBuilder) { func (opts *QueryOpts) WriteCondition(builder *StatementBuilder) {
if opts.Condition == nil { if opts.Condition == nil {
return return

View File

@@ -18,6 +18,39 @@ type instanceDomain struct {
// repository // repository
// ------------------------------------------------------------- // -------------------------------------------------------------
const queryInstanceDomainStmt = `SELECT instance_id, domain, is_verified, is_primary, verification_type, created_at, updated_at ` +
`FROM zitadel.instance_domains`
// Get implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method ([domain.InstanceRepository]).Get of instanceDomain.instance.
func (i *instanceDomain) Get(ctx context.Context, opts ...database.QueryOption) (*domain.InstanceDomain, error) {
options := new(database.QueryOpts)
for _, opt := range opts {
opt(options)
}
var builder database.StatementBuilder
builder.WriteString(queryInstanceDomainStmt)
options.Write(&builder)
return scanInstanceDomain(ctx, i.client, &builder)
}
// List implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method ([domain.InstanceRepository]).List of instanceDomain.instance.
func (i *instanceDomain) List(ctx context.Context, opts ...database.QueryOption) ([]*domain.InstanceDomain, error) {
options := new(database.QueryOpts)
for _, opt := range opts {
opt(options)
}
var builder database.StatementBuilder
builder.WriteString(queryInstanceDomainStmt)
options.Write(&builder)
return scanInstanceDomains(ctx, i.client, &builder)
}
// Add implements [domain.InstanceDomainRepository]. // Add implements [domain.InstanceDomainRepository].
func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDomain) error { func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDomain) error {
var builder database.StatementBuilder var builder database.StatementBuilder
@@ -42,7 +75,7 @@ func (i *instanceDomain) Remove(ctx context.Context, condition database.Conditio
} }
// Update implements [domain.InstanceDomainRepository]. // Update implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method (instance).Update of instanceDomain.instance. // Subtle: this method shadows the method ([domain.InstanceRepository]).Update of instanceDomain.instance.
func (i *instanceDomain) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error) { func (i *instanceDomain) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error) {
var builder database.StatementBuilder var builder database.StatementBuilder
@@ -102,7 +135,7 @@ func (i instanceDomain) IsVerifiedCondition(isVerified bool) database.Condition
// ------------------------------------------------------------- // -------------------------------------------------------------
// CreatedAtColumn implements [domain.InstanceDomainRepository]. // CreatedAtColumn implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method (instance).CreatedAtColumn of instanceDomain.instance. // Subtle: this method shadows the method ([domain.InstanceRepository]).CreatedAtColumn of instanceDomain.instance.
func (instanceDomain) CreatedAtColumn() database.Column { func (instanceDomain) CreatedAtColumn() database.Column {
return database.NewColumn("created_at") return database.NewColumn("created_at")
} }
@@ -128,7 +161,7 @@ func (instanceDomain) IsVerifiedColumn() database.Column {
} }
// UpdatedAtColumn implements [domain.InstanceDomainRepository]. // UpdatedAtColumn implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method (instance).UpdatedAtColumn of instanceDomain.instance. // Subtle: this method shadows the method ([domain.InstanceRepository]).UpdatedAtColumn of instanceDomain.instance.
func (instanceDomain) UpdatedAtColumn() database.Column { func (instanceDomain) UpdatedAtColumn() database.Column {
return database.NewColumn("updated_at") return database.NewColumn("updated_at")
} }
@@ -146,3 +179,30 @@ func (instanceDomain) IsGeneratedColumn() database.Column {
// ------------------------------------------------------------- // -------------------------------------------------------------
// scanners // scanners
// ------------------------------------------------------------- // -------------------------------------------------------------
func scanInstanceDomains(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) ([]*domain.InstanceDomain, error) {
rows, err := querier.Query(ctx, builder.String(), builder.Args()...)
if err != nil {
return nil, err
}
var instanceDomains []*domain.InstanceDomain
if err := rows.(database.CollectableRows).Collect(&instanceDomains); err != nil {
return nil, err
}
return instanceDomains, nil
}
func scanInstanceDomain(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) (*domain.InstanceDomain, error) {
rows, err := querier.Query(ctx, builder.String(), builder.Args()...)
if err != nil {
return nil, err
}
instanceDomain := &domain.InstanceDomain{}
if err := rows.(database.CollectableRows).CollectExactlyOneRow(instanceDomain); err != nil {
return nil, err
}
return instanceDomain, nil
}

View File

@@ -4,8 +4,6 @@ import (
"context" "context"
"errors" "errors"
"github.com/jackc/pgx/v5/pgconn"
"github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database"
) )
@@ -73,44 +71,9 @@ func (o *org) Create(ctx context.Context, organization *domain.Organization) err
builder.AppendArgs(organization.ID, organization.Name, organization.InstanceID, organization.State) builder.AppendArgs(organization.ID, organization.Name, organization.InstanceID, organization.State)
builder.WriteString(createOrganizationStmt) builder.WriteString(createOrganizationStmt)
err := o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt) return o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt)
if err != nil {
return checkCreateOrgErr(err)
}
return nil
} }
func checkCreateOrgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
return err
}
// constraint violation
if pgErr.Code == "23514" {
if pgErr.ConstraintName == "organizations_name_check" {
return errors.New("organization name not provided")
}
if pgErr.ConstraintName == "organizations_id_check" {
return errors.New("organization id not provided")
}
}
// duplicate
if pgErr.Code == "23505" {
if pgErr.ConstraintName == "organizations_pkey" {
return errors.New("organization id already exists")
}
if pgErr.ConstraintName == "org_unique_instance_id_name_idx" {
return errors.New("organization name already exists for instance")
}
}
// invalid instance id
if pgErr.Code == "23503" {
if pgErr.ConstraintName == "organizations_instance_id_fkey" {
return errors.New("invalid instance id")
}
}
return err
}
// Update implements [domain.OrganizationRepository]. // Update implements [domain.OrganizationRepository].
func (o *org) Update(ctx context.Context, id domain.OrgIdentifierCondition, instanceID string, changes ...database.Change) (int64, error) { func (o *org) Update(ctx context.Context, id domain.OrgIdentifierCondition, instanceID string, changes ...database.Change) (int64, error) {

View File

@@ -14,10 +14,45 @@ type orgDomain struct {
*org *org
} }
// ------------------------------------------------------------- // -------------------------------------------------------------
// repository // repository
// ------------------------------------------------------------- // -------------------------------------------------------------
const queryOrganizationDomainStmt = `SELECT instance_id, org_id, domain, is_verified, is_primary, verification_type, created_at, updated_at ` +
`FROM zitadel.organization_domains`
// Get implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method ([domain.OrganizationRepository]).Get of orgDomain.org.
func (o *orgDomain) Get(ctx context.Context, opts ...database.QueryOption) (*domain.OrganizationDomain, error) {
options := new(database.QueryOpts)
for _, opt := range opts {
opt(options)
}
var builder database.StatementBuilder
builder.WriteString(queryOrganizationDomainStmt)
options.Write(&builder)
return scanOrganizationDomain(ctx, o.client, &builder)
}
// List implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method ([domain.OrganizationRepository]).List of orgDomain.org.
func (o *orgDomain) List(ctx context.Context, opts ...database.QueryOption) ([]*domain.OrganizationDomain, error) {
options := new(database.QueryOpts)
for _, opt := range opts {
opt(options)
}
var builder database.StatementBuilder
builder.WriteString(queryOrganizationDomainStmt)
options.Write(&builder)
return scanOrganizationDomains(ctx, o.client, &builder)
}
// Add implements [domain.OrganizationDomainRepository]. // Add implements [domain.OrganizationDomainRepository].
func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomain) error { func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomain) error {
var builder database.StatementBuilder var builder database.StatementBuilder
@@ -32,7 +67,7 @@ func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomai
} }
// Update implements [domain.OrganizationDomainRepository]. // Update implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method (*org).Update of orgDomain.org. // Subtle: this method shadows the method ([domain.OrganizationRepository]).Update of orgDomain.org.
func (o *orgDomain) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error) { func (o *orgDomain) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error) {
var builder database.StatementBuilder var builder database.StatementBuilder
@@ -83,7 +118,7 @@ func (o orgDomain) DomainCondition(op database.TextOperation, domain string) dat
} }
// InstanceIDCondition implements [domain.OrganizationDomainRepository]. // InstanceIDCondition implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method (*org).InstanceIDCondition of orgDomain.org. // Subtle: this method shadows the method ([domain.OrganizationRepository]).InstanceIDCondition of orgDomain.org.
func (o orgDomain) InstanceIDCondition(instanceID string) database.Condition { func (o orgDomain) InstanceIDCondition(instanceID string) database.Condition {
return database.NewTextCondition(o.InstanceIDColumn(), database.TextOperationEqual, instanceID) return database.NewTextCondition(o.InstanceIDColumn(), database.TextOperationEqual, instanceID)
} }
@@ -108,7 +143,7 @@ func (o orgDomain) OrgIDCondition(orgID string) database.Condition {
// ------------------------------------------------------------- // -------------------------------------------------------------
// CreatedAtColumn implements [domain.OrganizationDomainRepository]. // CreatedAtColumn implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method (*org).CreatedAtColumn of orgDomain.org. // Subtle: this method shadows the method ([domain.OrganizationRepository]).CreatedAtColumn of orgDomain.org.
func (orgDomain) CreatedAtColumn() database.Column { func (orgDomain) CreatedAtColumn() database.Column {
return database.NewColumn("created_at") return database.NewColumn("created_at")
} }
@@ -119,7 +154,7 @@ func (orgDomain) DomainColumn() database.Column {
} }
// InstanceIDColumn implements [domain.OrganizationDomainRepository]. // InstanceIDColumn implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method (*org).InstanceIDColumn of orgDomain.org. // Subtle: this method shadows the method ([domain.OrganizationRepository]).InstanceIDColumn of orgDomain.org.
func (orgDomain) InstanceIDColumn() database.Column { func (orgDomain) InstanceIDColumn() database.Column {
return database.NewColumn("instance_id") return database.NewColumn("instance_id")
} }
@@ -140,7 +175,7 @@ func (orgDomain) OrgIDColumn() database.Column {
} }
// UpdatedAtColumn implements [domain.OrganizationDomainRepository]. // UpdatedAtColumn implements [domain.OrganizationDomainRepository].
// Subtle: this method shadows the method (*org).UpdatedAtColumn of orgDomain.org. // Subtle: this method shadows the method ([domain.OrganizationRepository]).UpdatedAtColumn of orgDomain.org.
func (orgDomain) UpdatedAtColumn() database.Column { func (orgDomain) UpdatedAtColumn() database.Column {
return database.NewColumn("updated_at") return database.NewColumn("updated_at")
} }
@@ -153,3 +188,29 @@ func (orgDomain) VerificationTypeColumn() database.Column {
// ------------------------------------------------------------- // -------------------------------------------------------------
// scanners // scanners
// ------------------------------------------------------------- // -------------------------------------------------------------
func scanOrganizationDomain(ctx context.Context, client database.Querier, builder *database.StatementBuilder) (*domain.OrganizationDomain, error) {
rows, err := client.Query(ctx, builder.String(), builder.Args()...)
if err != nil {
return nil, err
}
organizationDomain := &domain.OrganizationDomain{}
if err := rows.(database.CollectableRows).CollectExactlyOneRow(organizationDomain); err != nil {
return nil, err
}
return organizationDomain, nil
}
func scanOrganizationDomains(ctx context.Context, client database.Querier, builder *database.StatementBuilder) ([]*domain.OrganizationDomain, error) {
rows, err := client.Query(ctx, builder.String(), builder.Args()...)
if err != nil {
return nil, err
}
var organizationDomains []*domain.OrganizationDomain
if err := rows.(database.CollectableRows).Collect(&organizationDomains); err != nil {
return nil, err
}
return organizationDomains, nil
}

View File

@@ -24,7 +24,7 @@ func (b *StatementBuilder) WriteArg(arg any) {
b.WriteString(b.AppendArg(arg)) b.WriteString(b.AppendArg(arg))
} }
// AppebdArg adds the argument to the statement and returns the placeholder. // AppendArg adds the argument to the statement and returns the placeholder.
func (b *StatementBuilder) AppendArg(arg any) (placeholder string) { func (b *StatementBuilder) AppendArg(arg any) (placeholder string) {
if b.existingArgs == nil { if b.existingArgs == nil {
b.existingArgs = make(map[any]string) b.existingArgs = make(map[any]string)