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
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(ctx context.Context, domain *AddInstanceDomain) error
// 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
}
// 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
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(ctx context.Context, domain *AddOrganizationDomain) error
// Update updates an existing domain in the organization.

View File

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

View File

@@ -18,6 +18,39 @@ type instanceDomain struct {
// 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].
func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDomain) error {
var builder database.StatementBuilder
@@ -42,7 +75,7 @@ func (i *instanceDomain) Remove(ctx context.Context, condition database.Conditio
}
// 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) {
var builder database.StatementBuilder
@@ -102,7 +135,7 @@ func (i instanceDomain) IsVerifiedCondition(isVerified bool) database.Condition
// -------------------------------------------------------------
// 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 {
return database.NewColumn("created_at")
}
@@ -128,7 +161,7 @@ func (instanceDomain) IsVerifiedColumn() database.Column {
}
// 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 {
return database.NewColumn("updated_at")
}
@@ -146,3 +179,30 @@ func (instanceDomain) IsGeneratedColumn() database.Column {
// -------------------------------------------------------------
// 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"
"errors"
"github.com/jackc/pgx/v5/pgconn"
"github.com/zitadel/zitadel/backend/v3/domain"
"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.WriteString(createOrganizationStmt)
err := o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt)
if err != nil {
return checkCreateOrgErr(err)
}
return nil
return o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt)
}
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].
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
}
// -------------------------------------------------------------
// 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].
func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomain) error {
var builder database.StatementBuilder
@@ -32,7 +67,7 @@ func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomai
}
// 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) {
var builder database.StatementBuilder
@@ -83,7 +118,7 @@ func (o orgDomain) DomainCondition(op database.TextOperation, domain string) dat
}
// 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 {
return database.NewTextCondition(o.InstanceIDColumn(), database.TextOperationEqual, instanceID)
}
@@ -108,7 +143,7 @@ func (o orgDomain) OrgIDCondition(orgID string) database.Condition {
// -------------------------------------------------------------
// 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 {
return database.NewColumn("created_at")
}
@@ -119,7 +154,7 @@ func (orgDomain) DomainColumn() database.Column {
}
// 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 {
return database.NewColumn("instance_id")
}
@@ -140,7 +175,7 @@ func (orgDomain) OrgIDColumn() database.Column {
}
// 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 {
return database.NewColumn("updated_at")
}
@@ -153,3 +188,29 @@ func (orgDomain) VerificationTypeColumn() database.Column {
// -------------------------------------------------------------
// 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))
}
// 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) {
if b.existingArgs == nil {
b.existingArgs = make(map[any]string)