mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-08 07:05:12 +00:00
refactor: database interaction and error handling (#10762)
This pull request introduces a significant refactoring of the database interaction layer, focusing on improving explicitness, transactional control, and error handling. The core change is the removal of the stateful `QueryExecutor` from repository instances. Instead, it is now passed as an argument to each method that interacts with the database. This change makes transaction management more explicit and flexible, as the same repository instance can be used with a database pool or a specific transaction without needing to be re-instantiated. ### Key Changes - **Explicit `QueryExecutor` Passing:** - All repository methods (`Get`, `List`, `Create`, `Update`, `Delete`, etc.) in `InstanceRepository`, `OrganizationRepository`, `UserRepository`, and their sub-repositories now require a `database.QueryExecutor` (e.g., a `*pgxpool.Pool` or `pgx.Tx`) as the first argument. - Repository constructors no longer accept a `QueryExecutor`. For example, `repository.InstanceRepository(pool)` is now `repository.InstanceRepository()`. - **Enhanced Error Handling:** - A new `database.MissingConditionError` is introduced to enforce required query conditions, such as ensuring an `instance_id` is always present in `UPDATE` and `DELETE` operations. - The database error wrapper in the `postgres` package now correctly identifies and wraps `pgx.ErrTooManyRows` and similar errors from the `scany` library into a `database.MultipleRowsFoundError`. - **Improved Database Conditions:** - The `database.Condition` interface now includes a `ContainsColumn(Column) bool` method. This allows for runtime checks to ensure that critical filters (like `instance_id`) are included in a query, preventing accidental cross-tenant data modification. - A new `database.Exists()` condition has been added to support `EXISTS` subqueries, enabling more complex filtering logic, such as finding an organization that has a specific domain. - **Repository and Interface Refactoring:** - The method for loading related entities (e.g., domains for an organization) has been changed from a boolean flag (`Domains(true)`) to a more explicit, chainable method (`LoadDomains()`). This returns a new repository instance configured to load the sub-resource, promoting immutability. - The custom `OrgIdentifierCondition` has been removed in favor of using the standard `database.Condition` interface, simplifying the API. - **Code Cleanup and Test Updates:** - Unnecessary struct embeddings and metadata have been removed. - All integration and repository tests have been updated to reflect the new method signatures, passing the database pool or transaction object explicitly. - New tests have been added to cover the new `ExistsDomain` functionality and other enhancements. These changes make the data access layer more robust, predictable, and easier to work with, especially in the context of database transactions.
This commit is contained in:
@@ -3,19 +3,35 @@ package repository
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
type JSONArray[T any] []*T
|
||||
|
||||
func (a JSONArray[T]) Scan(src any) error {
|
||||
var ErrScanSource = errors.New("unsupported scan source")
|
||||
|
||||
func (a *JSONArray[T]) Scan(src any) (err error) {
|
||||
var rawJSON []byte
|
||||
switch s := src.(type) {
|
||||
case string:
|
||||
return json.Unmarshal([]byte(s), &a)
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
rawJSON = []byte(s)
|
||||
case []byte:
|
||||
return json.Unmarshal(s, &a)
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
rawJSON = s
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("unsupported scan source")
|
||||
return ErrScanSource
|
||||
}
|
||||
err = json.Unmarshal(rawJSON, a)
|
||||
if err != nil {
|
||||
return database.NewScanError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
||||
)
|
||||
|
||||
type testObject struct {
|
||||
ID string `json:"id"`
|
||||
Number int `json:"number"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
func TestJSONArray_Scan(t *testing.T) {
|
||||
type want struct {
|
||||
err error
|
||||
res []*testObject
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
src any
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
src: nil,
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[]byte with data",
|
||||
src: []byte(`[{"id":"1","number":1,"active":true}]`),
|
||||
want: want{
|
||||
err: nil,
|
||||
res: []*testObject{{ID: "1", Number: 1, Active: true}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[]byte without data",
|
||||
src: []byte(`[]`),
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil []byte",
|
||||
src: []byte(nil),
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty []byte",
|
||||
src: []byte{},
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "string with data",
|
||||
src: `[{"id":"1","number":1,"active":true}]`,
|
||||
want: want{
|
||||
err: nil,
|
||||
res: []*testObject{{ID: "1", Number: 1, Active: true}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "string without data",
|
||||
src: string(`[]`),
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
src: "",
|
||||
want: want{
|
||||
err: nil,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wrong type",
|
||||
src: []int{1, 2, 3},
|
||||
want: want{
|
||||
err: repository.ErrScanSource,
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "badly formatted JSON",
|
||||
src: []byte(`this is not a JSON`),
|
||||
want: want{
|
||||
err: new(database.ScanError),
|
||||
res: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var a repository.JSONArray[testObject]
|
||||
gotErr := a.Scan(tt.src)
|
||||
require.ErrorIs(t, gotErr, tt.want.err)
|
||||
require.Len(t, a, len(tt.want.res))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,20 @@ import (
|
||||
var _ domain.InstanceRepository = (*instance)(nil)
|
||||
|
||||
type instance struct {
|
||||
repository
|
||||
shouldLoadDomains bool
|
||||
domainRepo *instanceDomain
|
||||
domainRepo instanceDomain
|
||||
}
|
||||
|
||||
func InstanceRepository(client database.QueryExecutor) domain.InstanceRepository {
|
||||
return &instance{
|
||||
repository: repository{
|
||||
client: client,
|
||||
},
|
||||
}
|
||||
func InstanceRepository() domain.InstanceRepository {
|
||||
return new(instance)
|
||||
}
|
||||
|
||||
func (instance) qualifiedTableName() string {
|
||||
return "zitadel.instances"
|
||||
}
|
||||
|
||||
func (instance) unqualifiedTableName() string {
|
||||
return "instances"
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -37,7 +40,7 @@ const (
|
||||
)
|
||||
|
||||
// Get implements [domain.InstanceRepository].
|
||||
func (i *instance) Get(ctx context.Context, opts ...database.QueryOption) (*domain.Instance, error) {
|
||||
func (i instance) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.Instance, error) {
|
||||
opts = append(opts,
|
||||
i.joinDomains(),
|
||||
database.WithGroupBy(i.IDColumn()),
|
||||
@@ -52,11 +55,11 @@ func (i *instance) Get(ctx context.Context, opts ...database.QueryOption) (*doma
|
||||
builder.WriteString(queryInstanceStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanInstance(ctx, i.client, &builder)
|
||||
return scanInstance(ctx, client, &builder)
|
||||
}
|
||||
|
||||
// List implements [domain.InstanceRepository].
|
||||
func (i *instance) List(ctx context.Context, opts ...database.QueryOption) ([]*domain.Instance, error) {
|
||||
func (i instance) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.Instance, error) {
|
||||
opts = append(opts,
|
||||
i.joinDomains(),
|
||||
database.WithGroupBy(i.IDColumn()),
|
||||
@@ -71,27 +74,11 @@ func (i *instance) List(ctx context.Context, opts ...database.QueryOption) ([]*d
|
||||
builder.WriteString(queryInstanceStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanInstances(ctx, i.client, &builder)
|
||||
}
|
||||
|
||||
func (i *instance) joinDomains() database.QueryOption {
|
||||
columns := make([]database.Condition, 0, 2)
|
||||
columns = append(columns, database.NewColumnCondition(i.IDColumn(), i.Domains(false).InstanceIDColumn()))
|
||||
|
||||
// If domains should not be joined, we make sure to return null for the domain columns
|
||||
// the query optimizer of the dialect should optimize this away if no domains are requested
|
||||
if !i.shouldLoadDomains {
|
||||
columns = append(columns, database.IsNull(i.Domains(false).InstanceIDColumn()))
|
||||
}
|
||||
|
||||
return database.WithLeftJoin(
|
||||
"zitadel.instance_domains",
|
||||
database.And(columns...),
|
||||
)
|
||||
return scanInstances(ctx, client, &builder)
|
||||
}
|
||||
|
||||
// Create implements [domain.InstanceRepository].
|
||||
func (i *instance) Create(ctx context.Context, instance *domain.Instance) error {
|
||||
func (i instance) Create(ctx context.Context, client database.QueryExecutor, instance *domain.Instance) error {
|
||||
var (
|
||||
builder database.StatementBuilder
|
||||
createdAt, updatedAt any = database.DefaultInstruction, database.DefaultInstruction
|
||||
@@ -103,42 +90,46 @@ func (i *instance) Create(ctx context.Context, instance *domain.Instance) error
|
||||
updatedAt = instance.UpdatedAt
|
||||
}
|
||||
|
||||
builder.WriteString(`INSERT INTO zitadel.instances (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language, created_at, updated_at) VALUES (`)
|
||||
builder.WriteString(`INSERT INTO `)
|
||||
builder.WriteString(i.qualifiedTableName())
|
||||
builder.WriteString(` (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language, created_at, updated_at) VALUES (`)
|
||||
builder.WriteArgs(instance.ID, instance.Name, instance.DefaultOrgID, instance.IAMProjectID, instance.ConsoleClientID, instance.ConsoleAppID, instance.DefaultLanguage, createdAt, updatedAt)
|
||||
builder.WriteString(`) RETURNING created_at, updated_at`)
|
||||
|
||||
return i.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&instance.CreatedAt, &instance.UpdatedAt)
|
||||
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&instance.CreatedAt, &instance.UpdatedAt)
|
||||
}
|
||||
|
||||
// Update implements [domain.InstanceRepository].
|
||||
func (i instance) Update(ctx context.Context, id string, changes ...database.Change) (int64, error) {
|
||||
func (i instance) Update(ctx context.Context, client database.QueryExecutor, id string, changes ...database.Change) (int64, error) {
|
||||
if len(changes) == 0 {
|
||||
return 0, database.ErrNoChanges
|
||||
}
|
||||
if !database.Changes(changes).IsOnColumn(i.UpdatedAtColumn()) {
|
||||
changes = append(changes, database.NewChange(i.UpdatedAtColumn(), database.NullInstruction))
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
|
||||
builder.WriteString(`UPDATE zitadel.instances SET `)
|
||||
|
||||
database.Changes(changes).Write(&builder)
|
||||
|
||||
idCondition := i.IDCondition(id)
|
||||
writeCondition(&builder, idCondition)
|
||||
|
||||
stmt := builder.String()
|
||||
|
||||
return i.client.Exec(ctx, stmt, builder.Args()...)
|
||||
return client.Exec(ctx, stmt, builder.Args()...)
|
||||
}
|
||||
|
||||
// Delete implements [domain.InstanceRepository].
|
||||
func (i instance) Delete(ctx context.Context, id string) (int64, error) {
|
||||
func (i instance) Delete(ctx context.Context, client database.QueryExecutor, id string) (int64, error) {
|
||||
var builder database.StatementBuilder
|
||||
|
||||
builder.WriteString(`DELETE FROM zitadel.instances`)
|
||||
builder.WriteString(`DELETE FROM `)
|
||||
builder.WriteString(i.qualifiedTableName())
|
||||
|
||||
idCondition := i.IDCondition(id)
|
||||
writeCondition(&builder, idCondition)
|
||||
|
||||
return i.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -185,53 +176,77 @@ func (i instance) NameCondition(op database.TextOperation, name string) database
|
||||
return database.NewTextCondition(i.NameColumn(), op, name)
|
||||
}
|
||||
|
||||
// ExistsDomain creates a correlated [database.Exists] condition on instance_domains.
|
||||
// Use this filter to make sure the Instance returned contains a specific domain.
|
||||
// of the instance in the aggregated result.
|
||||
// Example usage:
|
||||
//
|
||||
// domainRepo := instanceRepo.Domains(true) // ensure domains are loaded/aggregated
|
||||
// instance, _ := instanceRepo.Get(ctx,
|
||||
// database.WithCondition(
|
||||
// database.And(
|
||||
// instanceRepo.InstanceIDCondition(instanceID),
|
||||
// instanceRepo.ExistsDomain(domainRepo.DomainCondition(database.TextOperationEqual, "example.com")),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
func (i instance) ExistsDomain(cond database.Condition) database.Condition {
|
||||
return database.Exists(
|
||||
i.domainRepo.qualifiedTableName(),
|
||||
database.And(
|
||||
database.NewColumnCondition(i.IDColumn(), i.domainRepo.InstanceIDColumn()),
|
||||
cond,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// columns
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// IDColumn implements [domain.instanceColumns].
|
||||
func (instance) IDColumn() database.Column {
|
||||
return database.NewColumn("instances", "id")
|
||||
func (i instance) IDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "id")
|
||||
}
|
||||
|
||||
// NameColumn implements [domain.instanceColumns].
|
||||
func (instance) NameColumn() database.Column {
|
||||
return database.NewColumn("instances", "name")
|
||||
func (i instance) NameColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "name")
|
||||
}
|
||||
|
||||
// CreatedAtColumn implements [domain.instanceColumns].
|
||||
func (instance) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn("instances", "created_at")
|
||||
func (i instance) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "created_at")
|
||||
}
|
||||
|
||||
// DefaultOrgIdColumn implements [domain.instanceColumns].
|
||||
func (instance) DefaultOrgIDColumn() database.Column {
|
||||
return database.NewColumn("instances", "default_org_id")
|
||||
func (i instance) DefaultOrgIDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "default_org_id")
|
||||
}
|
||||
|
||||
// IAMProjectIDColumn implements [domain.instanceColumns].
|
||||
func (instance) IAMProjectIDColumn() database.Column {
|
||||
return database.NewColumn("instances", "iam_project_id")
|
||||
func (i instance) IAMProjectIDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "iam_project_id")
|
||||
}
|
||||
|
||||
// ConsoleClientIDColumn implements [domain.instanceColumns].
|
||||
func (instance) ConsoleClientIDColumn() database.Column {
|
||||
return database.NewColumn("instances", "console_client_id")
|
||||
func (i instance) ConsoleClientIDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "console_client_id")
|
||||
}
|
||||
|
||||
// ConsoleAppIDColumn implements [domain.instanceColumns].
|
||||
func (instance) ConsoleAppIDColumn() database.Column {
|
||||
return database.NewColumn("instances", "console_app_id")
|
||||
func (i instance) ConsoleAppIDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "console_app_id")
|
||||
}
|
||||
|
||||
// DefaultLanguageColumn implements [domain.instanceColumns].
|
||||
func (instance) DefaultLanguageColumn() database.Column {
|
||||
return database.NewColumn("instances", "default_language")
|
||||
func (i instance) DefaultLanguageColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "default_language")
|
||||
}
|
||||
|
||||
// UpdatedAtColumn implements [domain.instanceColumns].
|
||||
func (instance) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn("instances", "updated_at")
|
||||
func (i instance) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "updated_at")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -282,19 +297,24 @@ func scanInstances(ctx context.Context, querier database.Querier, builder *datab
|
||||
// sub repositories
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// Domains implements [domain.InstanceRepository].
|
||||
func (i *instance) Domains(shouldLoad bool) domain.InstanceDomainRepository {
|
||||
if !i.shouldLoadDomains {
|
||||
i.shouldLoadDomains = shouldLoad
|
||||
func (i *instance) LoadDomains() domain.InstanceRepository {
|
||||
return &instance{
|
||||
shouldLoadDomains: true,
|
||||
}
|
||||
}
|
||||
|
||||
if i.domainRepo != nil {
|
||||
return i.domainRepo
|
||||
func (i *instance) joinDomains() database.QueryOption {
|
||||
columns := make([]database.Condition, 0, 2)
|
||||
columns = append(columns, database.NewColumnCondition(i.IDColumn(), i.domainRepo.InstanceIDColumn()))
|
||||
|
||||
// If domains should not be joined, we make sure to return null for the domain columns
|
||||
// the query optimizer of the dialect should optimize this away if no domains are requested
|
||||
if !i.shouldLoadDomains {
|
||||
columns = append(columns, database.IsNull(i.domainRepo.InstanceIDColumn()))
|
||||
}
|
||||
|
||||
i.domainRepo = &instanceDomain{
|
||||
repository: i.repository,
|
||||
instance: i,
|
||||
}
|
||||
return i.domainRepo
|
||||
return database.WithLeftJoin(
|
||||
i.domainRepo.qualifiedTableName(),
|
||||
database.And(columns...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,9 +10,18 @@ import (
|
||||
|
||||
var _ domain.InstanceDomainRepository = (*instanceDomain)(nil)
|
||||
|
||||
type instanceDomain struct {
|
||||
repository
|
||||
*instance
|
||||
type instanceDomain struct{}
|
||||
|
||||
func InstanceDomainRepository() domain.InstanceDomainRepository {
|
||||
return new(instanceDomain)
|
||||
}
|
||||
|
||||
func (instanceDomain) qualifiedTableName() string {
|
||||
return "zitadel.instance_domains"
|
||||
}
|
||||
|
||||
func (instanceDomain) unqualifiedTableName() string {
|
||||
return "instance_domains"
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -23,8 +32,7 @@ const queryInstanceDomainStmt = `SELECT instance_domains.instance_id, instance_d
|
||||
`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) {
|
||||
func (i instanceDomain) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.InstanceDomain, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
@@ -34,12 +42,11 @@ func (i *instanceDomain) Get(ctx context.Context, opts ...database.QueryOption)
|
||||
builder.WriteString(queryInstanceDomainStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanInstanceDomain(ctx, i.client, &builder)
|
||||
return scanInstanceDomain(ctx, 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) {
|
||||
func (i instanceDomain) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.InstanceDomain, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
@@ -49,11 +56,11 @@ func (i *instanceDomain) List(ctx context.Context, opts ...database.QueryOption)
|
||||
builder.WriteString(queryInstanceDomainStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanInstanceDomains(ctx, i.client, &builder)
|
||||
return scanInstanceDomains(ctx, client, &builder)
|
||||
}
|
||||
|
||||
// Add implements [domain.InstanceDomainRepository].
|
||||
func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDomain) error {
|
||||
func (i instanceDomain) Add(ctx context.Context, client database.QueryExecutor, domain *domain.AddInstanceDomain) error {
|
||||
var (
|
||||
builder database.StatementBuilder
|
||||
createdAt, updatedAt any = database.DefaultInstruction, database.DefaultInstruction
|
||||
@@ -69,33 +76,41 @@ func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDoma
|
||||
builder.WriteArgs(domain.InstanceID, domain.Domain, domain.IsPrimary, domain.IsGenerated, domain.Type, createdAt, updatedAt)
|
||||
builder.WriteString(`) RETURNING created_at, updated_at`)
|
||||
|
||||
return i.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&domain.CreatedAt, &domain.UpdatedAt)
|
||||
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&domain.CreatedAt, &domain.UpdatedAt)
|
||||
}
|
||||
|
||||
// Update implements [domain.InstanceDomainRepository].
|
||||
// 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, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) {
|
||||
if !condition.IsRestrictingColumn(i.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(i.InstanceIDColumn())
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return 0, database.ErrNoChanges
|
||||
}
|
||||
var builder database.StatementBuilder
|
||||
if !database.Changes(changes).IsOnColumn(i.UpdatedAtColumn()) {
|
||||
changes = append(changes, database.NewChange(i.UpdatedAtColumn(), database.NullInstruction))
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(`UPDATE zitadel.instance_domains SET `)
|
||||
database.Changes(changes).Write(&builder)
|
||||
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
return i.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// Remove implements [domain.InstanceDomainRepository].
|
||||
func (i *instanceDomain) Remove(ctx context.Context, condition database.Condition) (int64, error) {
|
||||
var builder database.StatementBuilder
|
||||
func (i instanceDomain) Remove(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) {
|
||||
if !condition.IsRestrictingColumn(i.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(i.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(`DELETE FROM zitadel.instance_domains WHERE `)
|
||||
condition.Write(&builder)
|
||||
|
||||
return i.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -146,40 +161,38 @@ func (i instanceDomain) TypeCondition(typ domain.DomainType) database.Condition
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// CreatedAtColumn implements [domain.InstanceDomainRepository].
|
||||
// Subtle: this method shadows the method ([domain.InstanceRepository]).CreatedAtColumn of instanceDomain.instance.
|
||||
func (instanceDomain) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "created_at")
|
||||
func (i instanceDomain) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "created_at")
|
||||
}
|
||||
|
||||
// DomainColumn implements [domain.InstanceDomainRepository].
|
||||
func (instanceDomain) DomainColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "domain")
|
||||
func (i instanceDomain) DomainColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "domain")
|
||||
}
|
||||
|
||||
// InstanceIDColumn implements [domain.InstanceDomainRepository].
|
||||
func (instanceDomain) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "instance_id")
|
||||
func (i instanceDomain) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "instance_id")
|
||||
}
|
||||
|
||||
// IsPrimaryColumn implements [domain.InstanceDomainRepository].
|
||||
func (instanceDomain) IsPrimaryColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "is_primary")
|
||||
func (i instanceDomain) IsPrimaryColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "is_primary")
|
||||
}
|
||||
|
||||
// UpdatedAtColumn implements [domain.InstanceDomainRepository].
|
||||
// Subtle: this method shadows the method ([domain.InstanceRepository]).UpdatedAtColumn of instanceDomain.instance.
|
||||
func (instanceDomain) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "updated_at")
|
||||
func (i instanceDomain) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "updated_at")
|
||||
}
|
||||
|
||||
// IsGeneratedColumn implements [domain.InstanceDomainRepository].
|
||||
func (instanceDomain) IsGeneratedColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "is_generated")
|
||||
func (i instanceDomain) IsGeneratedColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "is_generated")
|
||||
}
|
||||
|
||||
// TypeColumn implements [domain.InstanceDomainRepository].
|
||||
func (instanceDomain) TypeColumn() database.Column {
|
||||
return database.NewColumn("instance_domains", "type")
|
||||
func (i instanceDomain) TypeColumn() database.Column {
|
||||
return database.NewColumn(i.unqualifiedTableName(), "type")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -16,31 +15,43 @@ import (
|
||||
)
|
||||
|
||||
func TestAddInstanceDomain(t *testing.T) {
|
||||
// we take now here because the timestamp of the transaction is used to set the createdAt and updatedAt fields
|
||||
beforeAdd := time.Now()
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
Name: gofakeit.Name(),
|
||||
ID: gofakeit.NewCrypto().UUID(),
|
||||
Name: gofakeit.BeerName(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleClient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T, domainRepo domain.InstanceDomainRepository) *domain.AddInstanceDomain
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.AddInstanceDomain
|
||||
instanceDomain domain.AddInstanceDomain
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "happy path custom domain",
|
||||
instanceDomain: domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
Type: domain.DomainTypeCustom,
|
||||
IsPrimary: gu.Ptr(false),
|
||||
@@ -50,7 +61,7 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
{
|
||||
name: "happy path trusted domain",
|
||||
instanceDomain: domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
Type: domain.DomainTypeTrusted,
|
||||
},
|
||||
@@ -58,7 +69,7 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
{
|
||||
name: "add primary domain",
|
||||
instanceDomain: domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
Type: domain.DomainTypeCustom,
|
||||
IsPrimary: gu.Ptr(true),
|
||||
@@ -68,7 +79,7 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
{
|
||||
name: "add custom domain without domain name",
|
||||
instanceDomain: domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: "",
|
||||
Type: domain.DomainTypeCustom,
|
||||
IsPrimary: gu.Ptr(false),
|
||||
@@ -79,7 +90,7 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
{
|
||||
name: "add trusted domain without domain name",
|
||||
instanceDomain: domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: "",
|
||||
Type: domain.DomainTypeTrusted,
|
||||
},
|
||||
@@ -87,23 +98,23 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "add custom domain with same domain twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T, domainRepo domain.InstanceDomainRepository) *domain.AddInstanceDomain {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.AddInstanceDomain {
|
||||
domainName := gofakeit.DomainName()
|
||||
|
||||
instanceDomain := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: domainName,
|
||||
Type: domain.DomainTypeCustom,
|
||||
IsPrimary: gu.Ptr(false),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
}
|
||||
|
||||
err := domainRepo.Add(ctx, instanceDomain)
|
||||
err := domainRepo.Add(t.Context(), tx, instanceDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
// return same domain again
|
||||
return &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: domainName,
|
||||
Type: domain.DomainTypeCustom,
|
||||
IsPrimary: gu.Ptr(false),
|
||||
@@ -114,22 +125,20 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "add trusted domain with same domain twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T, domainRepo domain.InstanceDomainRepository) *domain.AddInstanceDomain {
|
||||
domainName := gofakeit.DomainName()
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.AddInstanceDomain {
|
||||
instanceDomain := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
Type: domain.DomainTypeTrusted,
|
||||
}
|
||||
|
||||
err := domainRepo.Add(ctx, instanceDomain)
|
||||
err := domainRepo.Add(t.Context(), tx, instanceDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
// return same domain again
|
||||
return &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName,
|
||||
InstanceID: instance.ID,
|
||||
Domain: instanceDomain.Domain,
|
||||
Type: domain.DomainTypeTrusted,
|
||||
}
|
||||
},
|
||||
@@ -196,26 +205,23 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// we take now here because the timestamp of the transaction is used to set the createdAt and updatedAt fields
|
||||
beforeAdd := time.Now()
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
err := savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
|
||||
var instanceDomain *domain.AddInstanceDomain
|
||||
if test.testFunc != nil {
|
||||
instanceDomain = test.testFunc(ctx, t, domainRepo)
|
||||
instanceDomain = test.testFunc(t, savepoint)
|
||||
} else {
|
||||
instanceDomain = &test.instanceDomain
|
||||
}
|
||||
|
||||
err = domainRepo.Add(ctx, instanceDomain)
|
||||
err = domainRepo.Add(t.Context(), savepoint, instanceDomain)
|
||||
afterAdd := time.Now()
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
@@ -232,10 +238,21 @@ func TestAddInstanceDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetInstanceDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.NewCrypto().UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -243,38 +260,29 @@ func TestGetInstanceDomain(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domains
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domainName1 := gofakeit.DomainName()
|
||||
domainName2 := gofakeit.DomainName()
|
||||
|
||||
domain1 := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName1,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(true),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
domain2 := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName2,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(false),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), domain1)
|
||||
err = domainRepo.Add(t.Context(), tx, domain1)
|
||||
require.NoError(t, err)
|
||||
err = domainRepo.Add(t.Context(), domain2)
|
||||
err = domainRepo.Add(t.Context(), tx, domain2)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -289,19 +297,19 @@ func TestGetInstanceDomain(t *testing.T) {
|
||||
database.WithCondition(domainRepo.IsPrimaryCondition(true)),
|
||||
},
|
||||
expected: &domain.InstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName1,
|
||||
InstanceID: instance.ID,
|
||||
Domain: domain1.Domain,
|
||||
IsPrimary: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get by domain name",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.DomainCondition(database.TextOperationEqual, domainName2)),
|
||||
database.WithCondition(domainRepo.DomainCondition(database.TextOperationEqual, domain2.Domain)),
|
||||
},
|
||||
expected: &domain.InstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName2,
|
||||
InstanceID: instance.ID,
|
||||
Domain: domain2.Domain,
|
||||
IsPrimary: gu.Ptr(false),
|
||||
},
|
||||
},
|
||||
@@ -318,7 +326,7 @@ func TestGetInstanceDomain(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
result, err := domainRepo.Get(ctx, test.opts...)
|
||||
result, err := domainRepo.Get(ctx, tx, test.opts...)
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
return
|
||||
@@ -335,10 +343,21 @@ func TestGetInstanceDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceDomains(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.NewCrypto().UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -346,42 +365,35 @@ func TestListInstanceDomains(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add multiple domains
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domains := []domain.AddInstanceDomain{
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(true),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
},
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(false),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
},
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
Type: domain.DomainTypeTrusted,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range domains {
|
||||
err = domainRepo.Add(t.Context(), &domains[i])
|
||||
err = domainRepo.Add(t.Context(), tx, &domains[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -405,7 +417,7 @@ func TestListInstanceDomains(t *testing.T) {
|
||||
{
|
||||
name: "list by instance",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.InstanceIDCondition(instanceID)),
|
||||
database.WithCondition(domainRepo.InstanceIDCondition(instance.ID)),
|
||||
},
|
||||
expectedCount: 3,
|
||||
},
|
||||
@@ -422,12 +434,12 @@ func TestListInstanceDomains(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
results, err := domainRepo.List(ctx, test.opts...)
|
||||
results, err := domainRepo.List(ctx, tx, test.opts...)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, results, test.expectedCount)
|
||||
|
||||
for _, result := range results {
|
||||
assert.Equal(t, instanceID, result.InstanceID)
|
||||
assert.Equal(t, instance.ID, result.InstanceID)
|
||||
assert.NotEmpty(t, result.Domain)
|
||||
assert.NotEmpty(t, result.CreatedAt)
|
||||
assert.NotEmpty(t, result.UpdatedAt)
|
||||
@@ -437,10 +449,21 @@ func TestListInstanceDomains(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateInstanceDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -448,29 +471,19 @@ func TestUpdateInstanceDomain(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domain
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domainName := gofakeit.DomainName()
|
||||
instanceDomain := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(false),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), instanceDomain)
|
||||
err = domainRepo.Add(t.Context(), tx, instanceDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -481,31 +494,38 @@ func TestUpdateInstanceDomain(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "set primary",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 1,
|
||||
name: "set primary",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, instanceDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "update non-existent domain",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 0,
|
||||
name: "update non-existent domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{},
|
||||
expected: 0,
|
||||
err: database.ErrNoChanges,
|
||||
name: "no changes",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, instanceDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{},
|
||||
expected: 0,
|
||||
err: database.ErrNoChanges,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
rowsAffected, err := domainRepo.Update(ctx, test.condition, test.changes...)
|
||||
rowsAffected, err := domainRepo.Update(t.Context(), tx, test.condition, test.changes...)
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
return
|
||||
@@ -516,7 +536,7 @@ func TestUpdateInstanceDomain(t *testing.T) {
|
||||
|
||||
// verify changes were applied if rows were affected
|
||||
if rowsAffected > 0 && len(test.changes) > 0 {
|
||||
result, err := domainRepo.Get(ctx, database.WithCondition(test.condition))
|
||||
result, err := domainRepo.Get(t.Context(), tx, database.WithCondition(test.condition))
|
||||
require.NoError(t, err)
|
||||
|
||||
// We know changes were applied since rowsAffected > 0
|
||||
@@ -529,10 +549,21 @@ func TestUpdateInstanceDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveInstanceDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -540,37 +571,28 @@ func TestRemoveInstanceDomain(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domains
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domainName1 := gofakeit.DomainName()
|
||||
|
||||
domain1 := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
Domain: domainName1,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(true),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
domain2 := &domain.AddInstanceDomain{
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsPrimary: gu.Ptr(false),
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), domain1)
|
||||
err = domainRepo.Add(t.Context(), tx, domain1)
|
||||
require.NoError(t, err)
|
||||
err = domainRepo.Add(t.Context(), domain2)
|
||||
err = domainRepo.Add(t.Context(), tx, domain2)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -579,36 +601,43 @@ func TestRemoveInstanceDomain(t *testing.T) {
|
||||
expected int64
|
||||
}{
|
||||
{
|
||||
name: "remove by domain name",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName1),
|
||||
expected: 1,
|
||||
name: "remove by domain name",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, domain1.Domain),
|
||||
),
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "remove by primary condition",
|
||||
condition: domainRepo.IsPrimaryCondition(false),
|
||||
expected: 1, // domain2 should still exist and be non-primary
|
||||
name: "remove by primary condition",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.IsPrimaryCondition(false),
|
||||
),
|
||||
expected: 1, // domain2 should still exist and be non-primary
|
||||
},
|
||||
{
|
||||
name: "remove non-existent domain",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
expected: 0,
|
||||
name: "remove non-existent domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
),
|
||||
expected: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// count before removal
|
||||
beforeCount, err := domainRepo.List(ctx)
|
||||
beforeCount, err := domainRepo.List(t.Context(), tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
rowsAffected, err := domainRepo.Remove(ctx, test.condition)
|
||||
rowsAffected, err := domainRepo.Remove(t.Context(), tx, test.condition)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected, rowsAffected)
|
||||
|
||||
// verify removal
|
||||
afterCount, err := domainRepo.List(ctx)
|
||||
afterCount, err := domainRepo.List(t.Context(), tx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(beforeCount)-int(test.expected), len(afterCount))
|
||||
})
|
||||
@@ -616,8 +645,7 @@ func TestRemoveInstanceDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstanceDomainConditions(t *testing.T) {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -671,8 +699,7 @@ func TestInstanceDomainConditions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstanceDomainChanges(t *testing.T) {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -2,6 +2,7 @@ package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -16,9 +17,20 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateInstance(t *testing.T) {
|
||||
beforeCreate := time.Now()
|
||||
tx, err := pool.Begin(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(context.Background())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Instance
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Instance
|
||||
instance domain.Instance
|
||||
err error
|
||||
}{
|
||||
@@ -59,14 +71,10 @@ func TestCreateInstance(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "adding same instance twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
instanceName := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: instanceName,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -74,7 +82,9 @@ func TestCreateInstance(t *testing.T) {
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
// change the name to make sure same only the id clashes
|
||||
inst.Name = gofakeit.Name()
|
||||
require.NoError(t, err)
|
||||
@@ -84,7 +94,7 @@ func TestCreateInstance(t *testing.T) {
|
||||
},
|
||||
func() struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Instance
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Instance
|
||||
instance domain.Instance
|
||||
err error
|
||||
} {
|
||||
@@ -92,14 +102,12 @@ func TestCreateInstance(t *testing.T) {
|
||||
instanceName := gofakeit.Name()
|
||||
return struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Instance
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Instance
|
||||
instance domain.Instance
|
||||
err error
|
||||
}{
|
||||
name: "adding instance with same name twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: gofakeit.Name(),
|
||||
Name: instanceName,
|
||||
@@ -110,7 +118,7 @@ func TestCreateInstance(t *testing.T) {
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
// change the id
|
||||
@@ -135,11 +143,8 @@ func TestCreateInstance(t *testing.T) {
|
||||
{
|
||||
name: "adding instance with no id",
|
||||
instance: func() domain.Instance {
|
||||
// instanceId := gofakeit.Name()
|
||||
instanceName := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
// ID: instanceId,
|
||||
Name: instanceName,
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -153,19 +158,25 @@ func TestCreateInstance(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var instance *domain.Instance
|
||||
if tt.testFunc != nil {
|
||||
instance = tt.testFunc(ctx, t)
|
||||
instance = tt.testFunc(t, savepoint)
|
||||
} else {
|
||||
instance = &tt.instance
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
|
||||
// create instance
|
||||
beforeCreate := time.Now()
|
||||
err := instanceRepo.Create(ctx, instance)
|
||||
|
||||
err = instanceRepo.Create(t.Context(), tx, instance)
|
||||
assert.ErrorIs(t, err, tt.err)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -173,7 +184,7 @@ func TestCreateInstance(t *testing.T) {
|
||||
afterCreate := time.Now()
|
||||
|
||||
// check instance values
|
||||
instance, err = instanceRepo.Get(ctx,
|
||||
instance, err = instanceRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
instanceRepo.IDCondition(instance.ID),
|
||||
),
|
||||
@@ -194,22 +205,30 @@ func TestCreateInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateInstance(t *testing.T) {
|
||||
beforeUpdate := time.Now()
|
||||
tx, err := pool.Begin(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(context.Background())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Instance
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Instance
|
||||
rowsAffected int64
|
||||
getErr error
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
instanceName := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: instanceName,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -218,7 +237,7 @@ func TestUpdateInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
return &inst
|
||||
},
|
||||
@@ -226,14 +245,10 @@ func TestUpdateInstance(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update deleted instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
instanceName := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: instanceName,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -242,11 +257,11 @@ func TestUpdateInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete instance
|
||||
affectedRows, err := instanceRepo.Delete(ctx,
|
||||
affectedRows, err := instanceRepo.Delete(t.Context(), tx,
|
||||
inst.ID,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -258,11 +273,9 @@ func TestUpdateInstance(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update non existent instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceId := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
ID: gofakeit.UUID(),
|
||||
}
|
||||
return &inst
|
||||
},
|
||||
@@ -272,15 +285,11 @@ func TestUpdateInstance(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instance := tt.testFunc(t, tx)
|
||||
|
||||
instance := tt.testFunc(ctx, t)
|
||||
|
||||
beforeUpdate := time.Now()
|
||||
// update name
|
||||
newName := "new_" + instance.Name
|
||||
rowsAffected, err := instanceRepo.Update(ctx,
|
||||
rowsAffected, err := instanceRepo.Update(t.Context(), tx,
|
||||
instance.ID,
|
||||
instanceRepo.SetName(newName),
|
||||
)
|
||||
@@ -294,7 +303,7 @@ func TestUpdateInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// check instance values
|
||||
instance, err = instanceRepo.Get(ctx,
|
||||
instance, err = instanceRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
instanceRepo.IDCondition(instance.ID),
|
||||
),
|
||||
@@ -308,24 +317,31 @@ func TestUpdateInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetInstance(t *testing.T) {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
tx, err := pool.Begin(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(context.Background())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
domainRepo := repository.InstanceDomainRepository()
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Instance
|
||||
testFunc func(t *testing.T) *domain.Instance
|
||||
err error
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
func() test {
|
||||
instanceId := gofakeit.Name()
|
||||
return test{
|
||||
name: "happy path get using id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceName := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: instanceName,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.BeerName(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -334,7 +350,7 @@ func TestGetInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
return &inst
|
||||
},
|
||||
@@ -342,14 +358,10 @@ func TestGetInstance(t *testing.T) {
|
||||
}(),
|
||||
{
|
||||
name: "happy path including domains",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
instanceName := gofakeit.Name()
|
||||
|
||||
testFunc: func(t *testing.T) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: instanceName,
|
||||
ID: gofakeit.NewCrypto().UUID(),
|
||||
Name: gofakeit.BeerName(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
@@ -358,10 +370,9 @@ func TestGetInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
domainRepo := instanceRepo.Domains(false)
|
||||
d := &domain.AddInstanceDomain{
|
||||
InstanceID: inst.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
@@ -369,7 +380,7 @@ func TestGetInstance(t *testing.T) {
|
||||
IsGenerated: gu.Ptr(false),
|
||||
Type: domain.DomainTypeCustom,
|
||||
}
|
||||
err = domainRepo.Add(ctx, d)
|
||||
err = domainRepo.Add(t.Context(), tx, d)
|
||||
require.NoError(t, err)
|
||||
|
||||
inst.Domains = append(inst.Domains, &domain.InstanceDomain{
|
||||
@@ -387,7 +398,7 @@ func TestGetInstance(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "get non existent instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Instance {
|
||||
testFunc: func(t *testing.T) *domain.Instance {
|
||||
inst := domain.Instance{
|
||||
ID: "get non existent instance",
|
||||
}
|
||||
@@ -398,16 +409,13 @@ func TestGetInstance(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
|
||||
var instance *domain.Instance
|
||||
if tt.testFunc != nil {
|
||||
instance = tt.testFunc(ctx, t)
|
||||
instance = tt.testFunc(t)
|
||||
}
|
||||
|
||||
// check instance values
|
||||
returnedInstance, err := instanceRepo.Get(ctx,
|
||||
returnedInstance, err := instanceRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
instanceRepo.IDCondition(instance.ID),
|
||||
),
|
||||
@@ -434,28 +442,33 @@ func TestGetInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstance(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pool, stop, err := newEmbeddedDB(ctx)
|
||||
tx, err := pool.Begin(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
defer stop()
|
||||
defer func() {
|
||||
err := tx.Rollback(context.Background())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) []*domain.Instance
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) []*domain.Instance
|
||||
conditionClauses []database.Condition
|
||||
noInstanceReturned bool
|
||||
}
|
||||
tests := []test{
|
||||
{
|
||||
name: "happy path single instance no filter",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Instance {
|
||||
noOfInstances := 1
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -465,7 +478,7 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
@@ -476,14 +489,13 @@ func TestListInstance(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path multiple instance no filter",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Instance {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Instance {
|
||||
noOfInstances := 5
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -493,7 +505,7 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
@@ -503,17 +515,16 @@ func TestListInstance(t *testing.T) {
|
||||
},
|
||||
},
|
||||
func() test {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
instanceID := gofakeit.BeerName()
|
||||
return test{
|
||||
name: "instance filter on id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Instance {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Instance {
|
||||
noOfInstances := 1
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
ID: instanceID,
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -523,7 +534,7 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
@@ -531,21 +542,20 @@ func TestListInstance(t *testing.T) {
|
||||
|
||||
return instances
|
||||
},
|
||||
conditionClauses: []database.Condition{instanceRepo.IDCondition(instanceId)},
|
||||
conditionClauses: []database.Condition{instanceRepo.IDCondition(instanceID)},
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceName := gofakeit.Name()
|
||||
instanceName := gofakeit.BeerName()
|
||||
return test{
|
||||
name: "multiple instance filter on name",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Instance {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Instance {
|
||||
noOfInstances := 5
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: instanceName,
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -555,7 +565,7 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
@@ -569,14 +579,15 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := pool.Exec(ctx, "DELETE FROM zitadel.instances")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
instances := tt.testFunc(ctx, t)
|
||||
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
instances := tt.testFunc(t, savepoint)
|
||||
|
||||
var condition database.Condition
|
||||
if len(tt.conditionClauses) > 0 {
|
||||
@@ -584,13 +595,13 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
// check instance values
|
||||
returnedInstances, err := instanceRepo.List(ctx,
|
||||
returnedInstances, err := instanceRepo.List(t.Context(), tx,
|
||||
database.WithCondition(condition),
|
||||
database.WithOrderByAscending(instanceRepo.CreatedAtColumn()),
|
||||
database.WithOrderByAscending(instanceRepo.IDColumn()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
if tt.noInstanceReturned {
|
||||
assert.Nil(t, returnedInstances)
|
||||
assert.Len(t, returnedInstances, 0)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -609,42 +620,45 @@ func TestListInstance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteInstance(t *testing.T) {
|
||||
tx, err := pool.Begin(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(context.Background())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T)
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor)
|
||||
instanceID string
|
||||
noOfDeletedRows int64
|
||||
}
|
||||
tests := []test{
|
||||
func() test {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceId := gofakeit.Name()
|
||||
var noOfInstances int64 = 1
|
||||
instanceID := gofakeit.NewCrypto().UUID()
|
||||
return test{
|
||||
name: "happy path delete single instance filter id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) {
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) {
|
||||
inst := domain.Instance{
|
||||
ID: instanceID,
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
instanceID: instanceId,
|
||||
noOfDeletedRows: noOfInstances,
|
||||
instanceID: instanceID,
|
||||
noOfDeletedRows: 1,
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
@@ -655,40 +669,33 @@ func TestDeleteInstance(t *testing.T) {
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
instanceName := gofakeit.Name()
|
||||
instanceID := gofakeit.Name()
|
||||
return test{
|
||||
name: "deleted already deleted instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) {
|
||||
noOfInstances := 1
|
||||
instances := make([]*domain.Instance, noOfInstances)
|
||||
for i := range noOfInstances {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) {
|
||||
|
||||
inst := domain.Instance{
|
||||
ID: gofakeit.Name(),
|
||||
Name: instanceName,
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(ctx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
instances[i] = &inst
|
||||
inst := domain.Instance{
|
||||
ID: instanceID,
|
||||
Name: gofakeit.BeerName(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
|
||||
// create instance
|
||||
err := instanceRepo.Create(t.Context(), tx, &inst)
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete instance
|
||||
affectedRows, err := instanceRepo.Delete(ctx,
|
||||
instances[0].ID,
|
||||
affectedRows, err := instanceRepo.Delete(t.Context(), tx,
|
||||
inst.ID,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), affectedRows)
|
||||
},
|
||||
instanceID: instanceName,
|
||||
instanceID: instanceID,
|
||||
// this test should return 0 affected rows as the instance was already deleted
|
||||
noOfDeletedRows: 0,
|
||||
}
|
||||
@@ -696,22 +703,26 @@ func TestDeleteInstance(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if tt.testFunc != nil {
|
||||
tt.testFunc(ctx, t)
|
||||
tt.testFunc(t, savepoint)
|
||||
}
|
||||
|
||||
// delete instance
|
||||
noOfDeletedRows, err := instanceRepo.Delete(ctx,
|
||||
tt.instanceID,
|
||||
)
|
||||
noOfDeletedRows, err := instanceRepo.Delete(t.Context(), savepoint, tt.instanceID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, noOfDeletedRows, tt.noOfDeletedRows)
|
||||
|
||||
// check instance was deleted
|
||||
instance, err := instanceRepo.Get(ctx,
|
||||
instance, err := instanceRepo.Get(t.Context(), savepoint,
|
||||
database.WithCondition(
|
||||
instanceRepo.IDCondition(tt.instanceID),
|
||||
),
|
||||
|
||||
@@ -14,25 +14,24 @@ import (
|
||||
var _ domain.OrganizationRepository = (*org)(nil)
|
||||
|
||||
type org struct {
|
||||
repository
|
||||
shouldLoadDomains bool
|
||||
domainRepo domain.OrganizationDomainRepository
|
||||
domainRepo orgDomain
|
||||
}
|
||||
|
||||
func OrganizationRepository(client database.QueryExecutor) domain.OrganizationRepository {
|
||||
return &org{
|
||||
repository: repository{
|
||||
client: client,
|
||||
},
|
||||
}
|
||||
func (o org) unqualifiedTableName() string {
|
||||
return "organizations"
|
||||
}
|
||||
|
||||
func OrganizationRepository() domain.OrganizationRepository {
|
||||
return new(org)
|
||||
}
|
||||
|
||||
const queryOrganizationStmt = `SELECT organizations.id, organizations.name, organizations.instance_id, organizations.state, organizations.created_at, organizations.updated_at` +
|
||||
` , jsonb_agg(json_build_object('domain', org_domains.domain, 'isVerified', org_domains.is_verified, 'isPrimary', org_domains.is_primary, 'validationType', org_domains.validation_type, 'createdAt', org_domains.created_at, 'updatedAt', org_domains.updated_at)) FILTER (WHERE org_domains.org_id IS NOT NULL) AS domains` +
|
||||
` , jsonb_agg(json_build_object('instanceId', org_domains.instance_id, 'orgId', org_domains.org_id, 'domain', org_domains.domain, 'isVerified', org_domains.is_verified, 'isPrimary', org_domains.is_primary, 'validationType', org_domains.validation_type, 'createdAt', org_domains.created_at, 'updatedAt', org_domains.updated_at)) FILTER (WHERE org_domains.org_id IS NOT NULL) AS domains` +
|
||||
` FROM zitadel.organizations`
|
||||
|
||||
// Get implements [domain.OrganizationRepository].
|
||||
func (o *org) Get(ctx context.Context, opts ...database.QueryOption) (*domain.Organization, error) {
|
||||
func (o org) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.Organization, error) {
|
||||
opts = append(opts,
|
||||
o.joinDomains(),
|
||||
database.WithGroupBy(o.InstanceIDColumn(), o.IDColumn()),
|
||||
@@ -43,15 +42,19 @@ func (o *org) Get(ctx context.Context, opts ...database.QueryOption) (*domain.Or
|
||||
opt(options)
|
||||
}
|
||||
|
||||
if !options.Condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return nil, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(queryOrganizationStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanOrganization(ctx, o.client, &builder)
|
||||
return scanOrganization(ctx, client, &builder)
|
||||
}
|
||||
|
||||
// List implements [domain.OrganizationRepository].
|
||||
func (o *org) List(ctx context.Context, opts ...database.QueryOption) ([]*domain.Organization, error) {
|
||||
func (o org) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.Organization, error) {
|
||||
opts = append(opts,
|
||||
o.joinDomains(),
|
||||
database.WithGroupBy(o.InstanceIDColumn(), o.IDColumn()),
|
||||
@@ -62,30 +65,15 @@ func (o *org) List(ctx context.Context, opts ...database.QueryOption) ([]*domain
|
||||
opt(options)
|
||||
}
|
||||
|
||||
if !options.Condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return nil, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(queryOrganizationStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanOrganizations(ctx, o.client, &builder)
|
||||
}
|
||||
|
||||
func (o *org) joinDomains() database.QueryOption {
|
||||
columns := make([]database.Condition, 0, 3)
|
||||
columns = append(columns,
|
||||
database.NewColumnCondition(o.InstanceIDColumn(), o.Domains(false).InstanceIDColumn()),
|
||||
database.NewColumnCondition(o.IDColumn(), o.Domains(false).OrgIDColumn()),
|
||||
)
|
||||
|
||||
// If domains should not be joined, we make sure to return null for the domain columns
|
||||
// the query optimizer of the dialect should optimize this away if no domains are requested
|
||||
if !o.shouldLoadDomains {
|
||||
columns = append(columns, database.IsNull(o.domainRepo.OrgIDColumn()))
|
||||
}
|
||||
|
||||
return database.WithLeftJoin(
|
||||
"zitadel.org_domains",
|
||||
database.And(columns...),
|
||||
)
|
||||
return scanOrganizations(ctx, client, &builder)
|
||||
}
|
||||
|
||||
const createOrganizationStmt = `INSERT INTO zitadel.organizations (id, name, instance_id, state)` +
|
||||
@@ -93,46 +81,48 @@ const createOrganizationStmt = `INSERT INTO zitadel.organizations (id, name, ins
|
||||
` RETURNING created_at, updated_at`
|
||||
|
||||
// Create implements [domain.OrganizationRepository].
|
||||
func (o *org) Create(ctx context.Context, organization *domain.Organization) error {
|
||||
func (o org) Create(ctx context.Context, client database.QueryExecutor, organization *domain.Organization) error {
|
||||
builder := database.StatementBuilder{}
|
||||
builder.AppendArgs(organization.ID, organization.Name, organization.InstanceID, organization.State)
|
||||
builder.WriteString(createOrganizationStmt)
|
||||
|
||||
return o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt)
|
||||
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&organization.CreatedAt, &organization.UpdatedAt)
|
||||
}
|
||||
|
||||
// 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, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) {
|
||||
if len(changes) == 0 {
|
||||
return 0, database.ErrNoChanges
|
||||
}
|
||||
builder := database.StatementBuilder{}
|
||||
if !condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
if !database.Changes(changes).IsOnColumn(o.UpdatedAtColumn()) {
|
||||
changes = append(changes, database.NewChange(o.UpdatedAtColumn(), database.NullInstruction))
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(`UPDATE zitadel.organizations SET `)
|
||||
|
||||
instanceIDCondition := o.InstanceIDCondition(instanceID)
|
||||
|
||||
conditions := []database.Condition{id, instanceIDCondition}
|
||||
database.Changes(changes).Write(&builder)
|
||||
writeCondition(&builder, database.And(conditions...))
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
stmt := builder.String()
|
||||
|
||||
rowsAffected, err := o.client.Exec(ctx, stmt, builder.Args()...)
|
||||
rowsAffected, err := client.Exec(ctx, stmt, builder.Args()...)
|
||||
return rowsAffected, err
|
||||
}
|
||||
|
||||
// Delete implements [domain.OrganizationRepository].
|
||||
func (o *org) Delete(ctx context.Context, id domain.OrgIdentifierCondition, instanceID string) (int64, error) {
|
||||
builder := database.StatementBuilder{}
|
||||
func (o org) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) {
|
||||
if !condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(`DELETE FROM zitadel.organizations`)
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
instanceIDCondition := o.InstanceIDCondition(instanceID)
|
||||
|
||||
conditions := []database.Condition{id, instanceIDCondition}
|
||||
writeCondition(&builder, database.And(conditions...))
|
||||
|
||||
return o.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -154,13 +144,13 @@ func (o org) SetState(state domain.OrgState) database.Change {
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// IDCondition implements [domain.organizationConditions].
|
||||
func (o org) IDCondition(id string) domain.OrgIdentifierCondition {
|
||||
func (o org) IDCondition(id string) database.Condition {
|
||||
return database.NewTextCondition(o.IDColumn(), database.TextOperationEqual, id)
|
||||
}
|
||||
|
||||
// NameCondition implements [domain.organizationConditions].
|
||||
func (o org) NameCondition(name string) domain.OrgIdentifierCondition {
|
||||
return database.NewTextCondition(o.NameColumn(), database.TextOperationEqual, name)
|
||||
func (o org) NameCondition(op database.TextOperation, name string) database.Condition {
|
||||
return database.NewTextCondition(o.NameColumn(), op, name)
|
||||
}
|
||||
|
||||
// InstanceIDCondition implements [domain.organizationConditions].
|
||||
@@ -173,38 +163,62 @@ func (o org) StateCondition(state domain.OrgState) database.Condition {
|
||||
return database.NewTextCondition(o.StateColumn(), database.TextOperationEqual, state.String())
|
||||
}
|
||||
|
||||
// ExistsDomain creates a correlated [database.Exists] condition on org_domains.
|
||||
// Use this filter to make sure the organization returned contains a specific domain.
|
||||
// Example usage:
|
||||
//
|
||||
// domainRepo := orgRepo.Domains(true) // ensure domains are loaded/aggregated
|
||||
// org, _ := orgRepo.Get(ctx,
|
||||
// database.WithCondition(
|
||||
// database.And(
|
||||
// orgRepo.InstanceIDCondition(instanceID),
|
||||
// orgRepo.ExistsDomain(domainRepo.DomainCondition(database.TextOperationEqual, "example.com")),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
func (o org) ExistsDomain(cond database.Condition) database.Condition {
|
||||
return database.Exists(
|
||||
o.domainRepo.qualifiedTableName(),
|
||||
database.And(
|
||||
database.NewColumnCondition(o.InstanceIDColumn(), o.domainRepo.InstanceIDColumn()),
|
||||
database.NewColumnCondition(o.IDColumn(), o.domainRepo.OrgIDColumn()),
|
||||
cond,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// columns
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// IDColumn implements [domain.organizationColumns].
|
||||
func (org) IDColumn() database.Column {
|
||||
return database.NewColumn("organizations", "id")
|
||||
func (o org) IDColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "id")
|
||||
}
|
||||
|
||||
// NameColumn implements [domain.organizationColumns].
|
||||
func (org) NameColumn() database.Column {
|
||||
return database.NewColumn("organizations", "name")
|
||||
func (o org) NameColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "name")
|
||||
}
|
||||
|
||||
// InstanceIDColumn implements [domain.organizationColumns].
|
||||
func (org) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn("organizations", "instance_id")
|
||||
func (o org) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "instance_id")
|
||||
}
|
||||
|
||||
// StateColumn implements [domain.organizationColumns].
|
||||
func (org) StateColumn() database.Column {
|
||||
return database.NewColumn("organizations", "state")
|
||||
func (o org) StateColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "state")
|
||||
}
|
||||
|
||||
// CreatedAtColumn implements [domain.organizationColumns].
|
||||
func (org) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn("organizations", "created_at")
|
||||
func (o org) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "created_at")
|
||||
}
|
||||
|
||||
// UpdatedAtColumn implements [domain.organizationColumns].
|
||||
func (org) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn("organizations", "updated_at")
|
||||
func (o org) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "updated_at")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -255,20 +269,27 @@ func scanOrganizations(ctx context.Context, querier database.Querier, builder *d
|
||||
// sub repositories
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// Domains implements [domain.OrganizationRepository].
|
||||
func (o *org) Domains(shouldLoad bool) domain.OrganizationDomainRepository {
|
||||
if !o.shouldLoadDomains {
|
||||
o.shouldLoadDomains = shouldLoad
|
||||
func (o org) LoadDomains() domain.OrganizationRepository {
|
||||
return &org{
|
||||
shouldLoadDomains: true,
|
||||
}
|
||||
}
|
||||
|
||||
if o.domainRepo != nil {
|
||||
return o.domainRepo
|
||||
}
|
||||
func (o org) joinDomains() database.QueryOption {
|
||||
columns := make([]database.Condition, 0, 3)
|
||||
columns = append(columns,
|
||||
database.NewColumnCondition(o.InstanceIDColumn(), o.domainRepo.InstanceIDColumn()),
|
||||
database.NewColumnCondition(o.IDColumn(), o.domainRepo.OrgIDColumn()),
|
||||
)
|
||||
|
||||
o.domainRepo = &orgDomain{
|
||||
repository: o.repository,
|
||||
org: o,
|
||||
// If domains should not be joined, we make sure to return null for the domain columns
|
||||
// the query optimizer of the dialect should optimize this away if no domains are requested
|
||||
if !o.shouldLoadDomains {
|
||||
columns = append(columns, database.IsNull(o.domainRepo.OrgIDColumn()))
|
||||
}
|
||||
|
||||
return o.domainRepo
|
||||
return database.WithLeftJoin(
|
||||
o.domainRepo.qualifiedTableName(),
|
||||
database.And(columns...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,9 +10,18 @@ import (
|
||||
|
||||
var _ domain.OrganizationDomainRepository = (*orgDomain)(nil)
|
||||
|
||||
type orgDomain struct {
|
||||
repository
|
||||
*org
|
||||
type orgDomain struct{}
|
||||
|
||||
func OrganizationDomainRepository() domain.OrganizationDomainRepository {
|
||||
return new(orgDomain)
|
||||
}
|
||||
|
||||
func (orgDomain) qualifiedTableName() string {
|
||||
return "zitadel.org_domains"
|
||||
}
|
||||
|
||||
func (orgDomain) unqualifiedTableName() string {
|
||||
return "org_domains"
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -24,36 +33,44 @@ const queryOrganizationDomainStmt = `SELECT instance_id, org_id, domain, is_veri
|
||||
|
||||
// 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) {
|
||||
func (o orgDomain) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.OrganizationDomain, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
if !options.Condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return nil, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(queryOrganizationDomainStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanOrganizationDomain(ctx, o.client, &builder)
|
||||
return scanOrganizationDomain(ctx, 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) {
|
||||
func (o orgDomain) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.OrganizationDomain, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
if !options.Condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return nil, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(queryOrganizationDomainStmt)
|
||||
options.Write(&builder)
|
||||
|
||||
return scanOrganizationDomains(ctx, o.client, &builder)
|
||||
return scanOrganizationDomains(ctx, client, &builder)
|
||||
}
|
||||
|
||||
// Add implements [domain.OrganizationDomainRepository].
|
||||
func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomain) error {
|
||||
func (o orgDomain) Add(ctx context.Context, client database.QueryExecutor, domain *domain.AddOrganizationDomain) error {
|
||||
var (
|
||||
builder database.StatementBuilder
|
||||
createdAt, updatedAt any = database.DefaultInstruction, database.DefaultInstruction
|
||||
@@ -69,33 +86,47 @@ func (o *orgDomain) Add(ctx context.Context, domain *domain.AddOrganizationDomai
|
||||
builder.WriteArgs(domain.InstanceID, domain.OrgID, domain.Domain, domain.IsVerified, domain.IsPrimary, domain.ValidationType, createdAt, updatedAt)
|
||||
builder.WriteString(`) RETURNING created_at, updated_at`)
|
||||
|
||||
return o.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&domain.CreatedAt, &domain.UpdatedAt)
|
||||
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&domain.CreatedAt, &domain.UpdatedAt)
|
||||
}
|
||||
|
||||
// Update implements [domain.OrganizationDomainRepository].
|
||||
// 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, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) {
|
||||
if len(changes) == 0 {
|
||||
return 0, database.ErrNoChanges
|
||||
}
|
||||
if !condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
if !condition.IsRestrictingColumn(o.OrgIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.OrgIDColumn())
|
||||
}
|
||||
if !database.Changes(changes).IsOnColumn(o.UpdatedAtColumn()) {
|
||||
changes = append(changes, database.NewChange(o.UpdatedAtColumn(), database.NullInstruction))
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
|
||||
builder.WriteString(`UPDATE zitadel.org_domains SET `)
|
||||
database.Changes(changes).Write(&builder)
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
return o.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// Remove implements [domain.OrganizationDomainRepository].
|
||||
func (o *orgDomain) Remove(ctx context.Context, condition database.Condition) (int64, error) {
|
||||
var builder database.StatementBuilder
|
||||
func (o orgDomain) Remove(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) {
|
||||
if !condition.IsRestrictingColumn(o.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.InstanceIDColumn())
|
||||
}
|
||||
if !condition.IsRestrictingColumn(o.OrgIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(o.OrgIDColumn())
|
||||
}
|
||||
|
||||
var builder database.StatementBuilder
|
||||
builder.WriteString(`DELETE FROM zitadel.org_domains `)
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
return o.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -158,45 +189,43 @@ func (o orgDomain) OrgIDCondition(orgID string) database.Condition {
|
||||
|
||||
// CreatedAtColumn implements [domain.OrganizationDomainRepository].
|
||||
// Subtle: this method shadows the method ([domain.OrganizationRepository]).CreatedAtColumn of orgDomain.org.
|
||||
func (orgDomain) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "created_at")
|
||||
func (o orgDomain) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "created_at")
|
||||
}
|
||||
|
||||
// DomainColumn implements [domain.OrganizationDomainRepository].
|
||||
func (orgDomain) DomainColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "domain")
|
||||
func (o orgDomain) DomainColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "domain")
|
||||
}
|
||||
|
||||
// InstanceIDColumn implements [domain.OrganizationDomainRepository].
|
||||
// Subtle: this method shadows the method ([domain.OrganizationRepository]).InstanceIDColumn of orgDomain.org.
|
||||
func (orgDomain) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "instance_id")
|
||||
func (o orgDomain) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "instance_id")
|
||||
}
|
||||
|
||||
// IsPrimaryColumn implements [domain.OrganizationDomainRepository].
|
||||
func (orgDomain) IsPrimaryColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "is_primary")
|
||||
func (o orgDomain) IsPrimaryColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "is_primary")
|
||||
}
|
||||
|
||||
// IsVerifiedColumn implements [domain.OrganizationDomainRepository].
|
||||
func (orgDomain) IsVerifiedColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "is_verified")
|
||||
func (o orgDomain) IsVerifiedColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "is_verified")
|
||||
}
|
||||
|
||||
// OrgIDColumn implements [domain.OrganizationDomainRepository].
|
||||
func (orgDomain) OrgIDColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "org_id")
|
||||
func (o orgDomain) OrgIDColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "org_id")
|
||||
}
|
||||
|
||||
// UpdatedAtColumn implements [domain.OrganizationDomainRepository].
|
||||
// Subtle: this method shadows the method ([domain.OrganizationRepository]).UpdatedAtColumn of orgDomain.org.
|
||||
func (orgDomain) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "updated_at")
|
||||
func (o orgDomain) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "updated_at")
|
||||
}
|
||||
|
||||
// ValidationTypeColumn implements [domain.OrganizationDomainRepository].
|
||||
func (orgDomain) ValidationTypeColumn() database.Column {
|
||||
return database.NewColumn("org_domains", "validation_type")
|
||||
func (o orgDomain) ValidationTypeColumn() database.Column {
|
||||
return database.NewColumn(o.unqualifiedTableName(), "validation_type")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
@@ -15,6 +14,15 @@ import (
|
||||
)
|
||||
|
||||
func TestAddOrganizationDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
@@ -26,8 +34,11 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create organization
|
||||
@@ -41,7 +52,7 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T, domainRepo domain.OrganizationDomainRepository) *domain.AddOrganizationDomain
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.AddOrganizationDomain
|
||||
organizationDomain domain.AddOrganizationDomain
|
||||
err error
|
||||
}{
|
||||
@@ -92,7 +103,7 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "add domain with same domain twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T, domainRepo domain.OrganizationDomainRepository) *domain.AddOrganizationDomain {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.AddOrganizationDomain {
|
||||
domainName := gofakeit.DomainName()
|
||||
|
||||
organizationDomain := &domain.AddOrganizationDomain{
|
||||
@@ -104,7 +115,7 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
}
|
||||
|
||||
err := domainRepo.Add(ctx, organizationDomain)
|
||||
err := domainRepo.Add(t.Context(), tx, organizationDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
// return same domain again
|
||||
@@ -169,28 +180,26 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
orgRepo := repository.OrganizationRepository(tx)
|
||||
err = orgRepo.Create(t.Context(), &organization)
|
||||
err = orgRepo.Create(t.Context(), savepoint, &organization)
|
||||
require.NoError(t, err)
|
||||
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
|
||||
var organizationDomain *domain.AddOrganizationDomain
|
||||
if test.testFunc != nil {
|
||||
organizationDomain = test.testFunc(ctx, t, domainRepo)
|
||||
organizationDomain = test.testFunc(t, savepoint)
|
||||
} else {
|
||||
organizationDomain = &test.organizationDomain
|
||||
}
|
||||
|
||||
err = domainRepo.Add(ctx, organizationDomain)
|
||||
err = domainRepo.Add(t.Context(), tx, organizationDomain)
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
return
|
||||
@@ -204,6 +213,16 @@ func TestAddOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOrganizationDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
@@ -225,29 +244,18 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRepo := repository.OrganizationRepository(tx)
|
||||
err = orgRepo.Create(t.Context(), &organization)
|
||||
err = orgRepo.Create(t.Context(), tx, &organization)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domains
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainName1 := gofakeit.DomainName()
|
||||
domainName2 := gofakeit.DomainName()
|
||||
|
||||
domain1 := &domain.AddOrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName1,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
@@ -255,15 +263,15 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
domain2 := &domain.AddOrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName2,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), domain1)
|
||||
err = domainRepo.Add(t.Context(), tx, domain1)
|
||||
require.NoError(t, err)
|
||||
err = domainRepo.Add(t.Context(), domain2)
|
||||
err = domainRepo.Add(t.Context(), tx, domain2)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -275,12 +283,15 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
{
|
||||
name: "get primary domain",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.IsPrimaryCondition(true)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instanceID),
|
||||
domainRepo.IsPrimaryCondition(true),
|
||||
)),
|
||||
},
|
||||
expected: &domain.OrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName1,
|
||||
Domain: domain1.Domain,
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
@@ -289,12 +300,15 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
{
|
||||
name: "get by domain name",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.DomainCondition(database.TextOperationEqual, domainName2)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instanceID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, domain2.Domain),
|
||||
)),
|
||||
},
|
||||
expected: &domain.OrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName2,
|
||||
Domain: domain2.Domain,
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
|
||||
@@ -303,13 +317,16 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
{
|
||||
name: "get by org ID",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.OrgIDCondition(orgID)),
|
||||
database.WithCondition(domainRepo.IsPrimaryCondition(true)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instanceID),
|
||||
domainRepo.OrgIDCondition(orgID),
|
||||
domainRepo.IsPrimaryCondition(true),
|
||||
)),
|
||||
},
|
||||
expected: &domain.OrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName1,
|
||||
Domain: domain1.Domain,
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
@@ -318,12 +335,15 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
{
|
||||
name: "get verified domain",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.IsVerifiedCondition(true)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instanceID),
|
||||
domainRepo.IsVerifiedCondition(true),
|
||||
)),
|
||||
},
|
||||
expected: &domain.OrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName1,
|
||||
Domain: domain1.Domain,
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
@@ -332,7 +352,10 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
{
|
||||
name: "get non-existent domain",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com")),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instanceID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
)),
|
||||
},
|
||||
err: new(database.NoRowFoundError),
|
||||
},
|
||||
@@ -340,9 +363,7 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
result, err := domainRepo.Get(ctx, test.opts...)
|
||||
result, err := domainRepo.Get(t.Context(), tx, test.opts...)
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
return
|
||||
@@ -362,10 +383,22 @@ func TestGetOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListOrganizationDomains(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -375,50 +408,40 @@ func TestListOrganizationDomains(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
orgID := gofakeit.UUID()
|
||||
organization := domain.Organization{
|
||||
ID: orgID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRepo := repository.OrganizationRepository(tx)
|
||||
err = orgRepo.Create(t.Context(), &organization)
|
||||
err = orgRepo.Create(t.Context(), tx, &organization)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add multiple domains
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domains := []domain.AddOrganizationDomain{
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
},
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
|
||||
},
|
||||
{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: true,
|
||||
IsPrimary: false,
|
||||
@@ -427,7 +450,7 @@ func TestListOrganizationDomains(t *testing.T) {
|
||||
}
|
||||
|
||||
for i := range domains {
|
||||
err = domainRepo.Add(t.Context(), &domains[i])
|
||||
err = domainRepo.Add(t.Context(), tx, &domains[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -437,42 +460,59 @@ func TestListOrganizationDomains(t *testing.T) {
|
||||
expectedCount int
|
||||
}{
|
||||
{
|
||||
name: "list all domains",
|
||||
opts: []database.QueryOption{},
|
||||
name: "list all domains",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.InstanceIDCondition(instance.ID)),
|
||||
},
|
||||
expectedCount: 3,
|
||||
},
|
||||
{
|
||||
name: "list verified domains",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.IsVerifiedCondition(true)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.IsVerifiedCondition(true),
|
||||
)),
|
||||
},
|
||||
expectedCount: 2,
|
||||
},
|
||||
{
|
||||
name: "list primary domains",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.IsPrimaryCondition(true)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.IsPrimaryCondition(true),
|
||||
)),
|
||||
},
|
||||
expectedCount: 1,
|
||||
},
|
||||
{
|
||||
name: "list by organization",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.OrgIDCondition(orgID)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
)),
|
||||
},
|
||||
expectedCount: 3,
|
||||
},
|
||||
{
|
||||
name: "list by instance",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.InstanceIDCondition(instanceID)),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
)),
|
||||
},
|
||||
expectedCount: 3,
|
||||
},
|
||||
{
|
||||
name: "list non-existent organization",
|
||||
opts: []database.QueryOption{
|
||||
database.WithCondition(domainRepo.OrgIDCondition("non-existent")),
|
||||
database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition("non-existent"),
|
||||
)),
|
||||
},
|
||||
expectedCount: 0,
|
||||
},
|
||||
@@ -482,13 +522,13 @@ func TestListOrganizationDomains(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
results, err := domainRepo.List(ctx, test.opts...)
|
||||
results, err := domainRepo.List(ctx, tx, test.opts...)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, results, test.expectedCount)
|
||||
|
||||
for _, result := range results {
|
||||
assert.Equal(t, instanceID, result.InstanceID)
|
||||
assert.Equal(t, orgID, result.OrgID)
|
||||
assert.Equal(t, instance.ID, result.InstanceID)
|
||||
assert.Equal(t, organization.ID, result.OrgID)
|
||||
assert.NotEmpty(t, result.Domain)
|
||||
assert.NotEmpty(t, result.CreatedAt)
|
||||
assert.NotEmpty(t, result.UpdatedAt)
|
||||
@@ -498,10 +538,22 @@ func TestListOrganizationDomains(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -511,41 +563,30 @@ func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
orgID := gofakeit.UUID()
|
||||
organization := domain.Organization{
|
||||
ID: orgID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRepo := repository.OrganizationRepository(tx)
|
||||
err = orgRepo.Create(t.Context(), &organization)
|
||||
err = orgRepo.Create(t.Context(), tx, &organization)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domain
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainName := gofakeit.DomainName()
|
||||
organizationDomain := &domain.AddOrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), organizationDomain)
|
||||
err = domainRepo.Add(t.Context(), tx, organizationDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -556,26 +597,42 @@ func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "set verified",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 1,
|
||||
name: "set verified",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "set primary",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 1,
|
||||
name: "set primary",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetPrimary()},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "set validation type",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{domainRepo.SetValidationType(domain.DomainValidationTypeHTTP)},
|
||||
expected: 1,
|
||||
name: "set validation type",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetValidationType(domain.DomainValidationTypeHTTP)},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "multiple changes",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
name: "multiple changes",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{
|
||||
domainRepo.SetVerified(),
|
||||
domainRepo.SetPrimary(),
|
||||
@@ -584,31 +641,41 @@ func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "update by org ID and domain",
|
||||
condition: database.And(domainRepo.OrgIDCondition(orgID), domainRepo.DomainCondition(database.TextOperationEqual, domainName)),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 1,
|
||||
name: "update by org ID and domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "update non-existent domain",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 0,
|
||||
name: "update non-existent domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
),
|
||||
changes: []database.Change{domainRepo.SetVerified()},
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
|
||||
changes: []database.Change{},
|
||||
expected: 0,
|
||||
err: database.ErrNoChanges,
|
||||
name: "no changes",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, organizationDomain.Domain),
|
||||
),
|
||||
changes: []database.Change{},
|
||||
expected: 0,
|
||||
err: database.ErrNoChanges,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
rowsAffected, err := domainRepo.Update(ctx, test.condition, test.changes...)
|
||||
rowsAffected, err := domainRepo.Update(t.Context(), tx, test.condition, test.changes...)
|
||||
if test.err != nil {
|
||||
assert.ErrorIs(t, err, test.err)
|
||||
return
|
||||
@@ -619,7 +686,7 @@ func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
|
||||
// verify changes were applied if rows were affected
|
||||
if rowsAffected > 0 && len(test.changes) > 0 {
|
||||
result, err := domainRepo.Get(ctx, database.WithCondition(test.condition))
|
||||
result, err := domainRepo.Get(t.Context(), tx, database.WithCondition(test.condition))
|
||||
require.NoError(t, err)
|
||||
|
||||
// We know changes were applied since rowsAffected > 0
|
||||
@@ -632,10 +699,22 @@ func TestUpdateOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveOrganizationDomain(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceID := gofakeit.UUID()
|
||||
instance := domain.Instance{
|
||||
ID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
@@ -645,53 +724,40 @@ func TestRemoveOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
orgID := gofakeit.UUID()
|
||||
organization := domain.Organization{
|
||||
ID: orgID,
|
||||
ID: gofakeit.UUID(),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceID,
|
||||
InstanceID: instance.ID,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, tx.Rollback(t.Context()))
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository(tx)
|
||||
err = instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRepo := repository.OrganizationRepository(tx)
|
||||
err = orgRepo.Create(t.Context(), &organization)
|
||||
err = orgRepo.Create(t.Context(), tx, &organization)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add domains
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainName1 := gofakeit.DomainName()
|
||||
domainName2 := gofakeit.DomainName()
|
||||
|
||||
domain1 := &domain.AddOrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName1,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
|
||||
}
|
||||
domain2 := &domain.AddOrganizationDomain{
|
||||
InstanceID: instanceID,
|
||||
OrgID: orgID,
|
||||
Domain: domainName2,
|
||||
InstanceID: instance.ID,
|
||||
OrgID: organization.ID,
|
||||
Domain: gofakeit.DomainName(),
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
|
||||
}
|
||||
|
||||
err = domainRepo.Add(t.Context(), domain1)
|
||||
err = domainRepo.Add(t.Context(), tx, domain1)
|
||||
require.NoError(t, err)
|
||||
err = domainRepo.Add(t.Context(), domain2)
|
||||
err = domainRepo.Add(t.Context(), tx, domain2)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -700,50 +766,70 @@ func TestRemoveOrganizationDomain(t *testing.T) {
|
||||
expected int64
|
||||
}{
|
||||
{
|
||||
name: "remove by domain name",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName1),
|
||||
expected: 1,
|
||||
name: "remove by domain name",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, domain1.Domain),
|
||||
),
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "remove by primary condition",
|
||||
condition: domainRepo.IsPrimaryCondition(false),
|
||||
expected: 1, // domain2 should still exist and be non-primary
|
||||
name: "remove by primary condition",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.IsPrimaryCondition(false),
|
||||
),
|
||||
expected: 1, // domain2 should still exist and be non-primary
|
||||
},
|
||||
{
|
||||
name: "remove by org ID and domain",
|
||||
condition: database.And(domainRepo.OrgIDCondition(orgID), domainRepo.DomainCondition(database.TextOperationEqual, domainName2)),
|
||||
expected: 1,
|
||||
name: "remove by org ID and domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, domain2.Domain),
|
||||
),
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "remove non-existent domain",
|
||||
condition: domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
expected: 0,
|
||||
name: "remove non-existent domain",
|
||||
condition: database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
|
||||
),
|
||||
expected: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
snapshot, err := tx.Begin(ctx)
|
||||
snapshot, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, snapshot.Rollback(ctx))
|
||||
err = snapshot.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Log("error during rollback:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
orgRepo := repository.OrganizationRepository(snapshot)
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
|
||||
// count before removal
|
||||
beforeCount, err := domainRepo.List(ctx)
|
||||
beforeCount, err := domainRepo.List(t.Context(), snapshot, database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
)))
|
||||
require.NoError(t, err)
|
||||
|
||||
rowsAffected, err := domainRepo.Remove(ctx, test.condition)
|
||||
rowsAffected, err := domainRepo.Remove(t.Context(), snapshot, test.condition)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected, rowsAffected)
|
||||
|
||||
// verify removal
|
||||
afterCount, err := domainRepo.List(ctx)
|
||||
afterCount, err := domainRepo.List(t.Context(), snapshot, database.WithCondition(database.And(
|
||||
domainRepo.InstanceIDCondition(instance.ID),
|
||||
domainRepo.OrgIDCondition(organization.ID),
|
||||
)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(beforeCount)-int(test.expected), len(afterCount))
|
||||
})
|
||||
@@ -751,8 +837,7 @@ func TestRemoveOrganizationDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOrganizationDomainConditions(t *testing.T) {
|
||||
orgRepo := repository.OrganizationRepository(pool)
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -811,8 +896,7 @@ func TestOrganizationDomainConditions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOrganizationDomainChanges(t *testing.T) {
|
||||
orgRepo := repository.OrganizationRepository(pool)
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -851,8 +935,7 @@ func TestOrganizationDomainChanges(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOrganizationDomainColumns(t *testing.T) {
|
||||
orgRepo := repository.OrganizationRepository(pool)
|
||||
domainRepo := orgRepo.Domains(false)
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -15,6 +15,18 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateOrganization(t *testing.T) {
|
||||
beforeCreate := time.Now()
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
organizationRepo := repository.OrganizationRepository()
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -26,13 +38,12 @@ func TestCreateOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Organization
|
||||
testFunc func(t *testing.T, client database.QueryExecutor) *domain.Organization
|
||||
organization domain.Organization
|
||||
err error
|
||||
}{
|
||||
@@ -67,8 +78,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "adding org with same id twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
@@ -79,7 +89,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
// change the name to make sure same only the id clashes
|
||||
org.Name = gofakeit.Name()
|
||||
@@ -89,8 +99,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "adding org with same name twice",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
@@ -101,7 +110,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
// change the id to make sure same name+instance causes an error
|
||||
org.ID = gofakeit.Name()
|
||||
@@ -111,7 +120,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
},
|
||||
func() struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Organization
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Organization
|
||||
organization domain.Organization
|
||||
err error
|
||||
} {
|
||||
@@ -120,12 +129,12 @@ func TestCreateOrganization(t *testing.T) {
|
||||
|
||||
return struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Organization
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) *domain.Organization
|
||||
organization domain.Organization
|
||||
err error
|
||||
}{
|
||||
name: "adding org with same name, different instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.Organization {
|
||||
// create instance
|
||||
instId := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -137,12 +146,9 @@ func TestCreateOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(ctx, &instance)
|
||||
err := instanceRepo.Create(t.Context(), tx, &instance)
|
||||
assert.Nil(t, err)
|
||||
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
org := domain.Organization{
|
||||
ID: gofakeit.Name(),
|
||||
Name: organizationName,
|
||||
@@ -150,7 +156,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
err = organizationRepo.Create(ctx, &org)
|
||||
err = organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// change the id to make it unique
|
||||
@@ -214,19 +220,25 @@ func TestCreateOrganization(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var organization *domain.Organization
|
||||
if tt.testFunc != nil {
|
||||
organization = tt.testFunc(ctx, t)
|
||||
organization = tt.testFunc(t, savepoint)
|
||||
} else {
|
||||
organization = &tt.organization
|
||||
}
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
// create organization
|
||||
beforeCreate := time.Now()
|
||||
err = organizationRepo.Create(ctx, organization)
|
||||
|
||||
err = organizationRepo.Create(t.Context(), savepoint, organization)
|
||||
assert.ErrorIs(t, err, tt.err)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -234,7 +246,7 @@ func TestCreateOrganization(t *testing.T) {
|
||||
afterCreate := time.Now()
|
||||
|
||||
// check organization values
|
||||
organization, err = organizationRepo.Get(ctx,
|
||||
organization, err = organizationRepo.Get(t.Context(), savepoint,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
organizationRepo.IDCondition(organization.ID),
|
||||
@@ -255,6 +267,19 @@ func TestCreateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateOrganization(t *testing.T) {
|
||||
beforeUpdate := time.Now()
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
organizationRepo := repository.OrganizationRepository()
|
||||
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -266,20 +291,18 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Organization
|
||||
testFunc func(t *testing.T) *domain.Organization
|
||||
update []database.Change
|
||||
rowsAffected int64
|
||||
}{
|
||||
{
|
||||
name: "happy path update name",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
@@ -291,7 +314,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// update with updated value
|
||||
@@ -303,7 +326,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update deleted organization",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
@@ -315,13 +338,15 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete instance
|
||||
_, err = organizationRepo.Delete(ctx,
|
||||
organizationRepo.IDCondition(org.ID),
|
||||
org.InstanceID,
|
||||
_, err = organizationRepo.Delete(t.Context(), tx,
|
||||
database.And(
|
||||
organizationRepo.InstanceIDCondition(org.InstanceID),
|
||||
organizationRepo.IDCondition(org.ID),
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -332,7 +357,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path change state",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
@@ -344,7 +369,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// update with updated value
|
||||
@@ -356,7 +381,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update non existent organization",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
|
||||
org := domain.Organization{
|
||||
@@ -370,16 +395,14 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
createdOrg := tt.testFunc(ctx, t)
|
||||
createdOrg := tt.testFunc(t)
|
||||
|
||||
// update org
|
||||
beforeUpdate := time.Now()
|
||||
rowsAffected, err := organizationRepo.Update(ctx,
|
||||
organizationRepo.IDCondition(createdOrg.ID),
|
||||
createdOrg.InstanceID,
|
||||
rowsAffected, err := organizationRepo.Update(t.Context(), tx,
|
||||
database.And(
|
||||
organizationRepo.InstanceIDCondition(createdOrg.InstanceID),
|
||||
organizationRepo.IDCondition(createdOrg.ID),
|
||||
),
|
||||
tt.update...,
|
||||
)
|
||||
afterUpdate := time.Now()
|
||||
@@ -392,7 +415,7 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// check organization values
|
||||
organization, err := organizationRepo.Get(ctx,
|
||||
organization, err := organizationRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
organizationRepo.IDCondition(createdOrg.ID),
|
||||
@@ -411,6 +434,19 @@ func TestUpdateOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOrganization(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
orgDomainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -422,11 +458,9 @@ func TestGetOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRepo := repository.OrganizationRepository(pool)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create organization
|
||||
// this org is created as an additional org which should NOT
|
||||
@@ -437,13 +471,13 @@ func TestGetOrganization(t *testing.T) {
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
err = orgRepo.Create(t.Context(), &org)
|
||||
err = orgRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) *domain.Organization
|
||||
orgIdentifierCondition domain.OrgIdentifierCondition
|
||||
testFunc func(t *testing.T) *domain.Organization
|
||||
orgIdentifierCondition database.Condition
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -452,7 +486,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
organizationId := gofakeit.Name()
|
||||
return test{
|
||||
name: "happy path get using id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
org := domain.Organization{
|
||||
@@ -463,7 +497,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := orgRepo.Create(ctx, &org)
|
||||
err := orgRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &org
|
||||
@@ -475,7 +509,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
organizationId := gofakeit.Name()
|
||||
return test{
|
||||
name: "happy path get using id including domain",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationName := gofakeit.Name()
|
||||
|
||||
org := domain.Organization{
|
||||
@@ -486,7 +520,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := orgRepo.Create(ctx, &org)
|
||||
err := orgRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
d := &domain.AddOrganizationDomain{
|
||||
@@ -496,7 +530,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
}
|
||||
err = orgRepo.Domains(false).Add(ctx, d)
|
||||
err = orgDomainRepo.Add(t.Context(), tx, d)
|
||||
require.NoError(t, err)
|
||||
|
||||
org.Domains = []*domain.OrganizationDomain{
|
||||
@@ -521,7 +555,7 @@ func TestGetOrganization(t *testing.T) {
|
||||
organizationName := gofakeit.Name()
|
||||
return test{
|
||||
name: "happy path get using name",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
organizationId := gofakeit.Name()
|
||||
|
||||
org := domain.Organization{
|
||||
@@ -532,39 +566,36 @@ func TestGetOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := orgRepo.Create(ctx, &org)
|
||||
err := orgRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &org
|
||||
},
|
||||
orgIdentifierCondition: orgRepo.NameCondition(organizationName),
|
||||
orgIdentifierCondition: orgRepo.NameCondition(database.TextOperationEqual, organizationName),
|
||||
}
|
||||
}(),
|
||||
{
|
||||
name: "get non existent organization",
|
||||
testFunc: func(ctx context.Context, t *testing.T) *domain.Organization {
|
||||
testFunc: func(t *testing.T) *domain.Organization {
|
||||
org := domain.Organization{
|
||||
ID: "non existent org",
|
||||
Name: "non existent org",
|
||||
}
|
||||
return &org
|
||||
},
|
||||
orgIdentifierCondition: orgRepo.NameCondition("non-existent-instance-name"),
|
||||
orgIdentifierCondition: orgRepo.NameCondition(database.TextOperationEqual, "non-existent-instance-name"),
|
||||
err: new(database.NoRowFoundError),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
orgRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
var org *domain.Organization
|
||||
if tt.testFunc != nil {
|
||||
org = tt.testFunc(ctx, t)
|
||||
org = tt.testFunc(t)
|
||||
}
|
||||
|
||||
// get org values
|
||||
returnedOrg, err := orgRepo.Get(ctx,
|
||||
returnedOrg, err := orgRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
tt.orgIdentifierCondition,
|
||||
@@ -592,11 +623,17 @@ func TestGetOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListOrganization(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
pool, stop, err := newEmbeddedDB(ctx)
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
defer stop()
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
defer func() {
|
||||
err := tx.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
organizationRepo := repository.OrganizationRepository()
|
||||
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
@@ -609,33 +646,32 @@ func TestListOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err = instanceRepo.Create(ctx, &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T) []*domain.Organization
|
||||
testFunc func(t *testing.T, tx database.QueryExecutor) []*domain.Organization
|
||||
conditionClauses []database.Condition
|
||||
noOrganizationReturned bool
|
||||
}
|
||||
tests := []test{
|
||||
{
|
||||
name: "happy path single organization no filter",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Organization {
|
||||
noOfOrganizations := 1
|
||||
organizations := make([]*domain.Organization, noOfOrganizations)
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
org := domain.Organization{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -646,20 +682,20 @@ func TestListOrganization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path multiple organization no filter",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Organization {
|
||||
noOfOrganizations := 5
|
||||
organizations := make([]*domain.Organization, noOfOrganizations)
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
org := domain.Organization{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -672,7 +708,7 @@ func TestListOrganization(t *testing.T) {
|
||||
organizationId := gofakeit.Name()
|
||||
return test{
|
||||
name: "organization filter on id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Organization {
|
||||
// create organization
|
||||
// this org is created as an additional org which should NOT
|
||||
// be returned in the results of this test case
|
||||
@@ -682,7 +718,7 @@ func TestListOrganization(t *testing.T) {
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
err = organizationRepo.Create(ctx, &org)
|
||||
err = organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
noOfOrganizations := 1
|
||||
@@ -697,7 +733,7 @@ func TestListOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -705,12 +741,15 @@ func TestListOrganization(t *testing.T) {
|
||||
|
||||
return organizations
|
||||
},
|
||||
conditionClauses: []database.Condition{organizationRepo.IDCondition(organizationId)},
|
||||
conditionClauses: []database.Condition{
|
||||
organizationRepo.InstanceIDCondition(instanceId),
|
||||
organizationRepo.IDCondition(organizationId),
|
||||
},
|
||||
}
|
||||
}(),
|
||||
{
|
||||
name: "multiple organization filter on state",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Organization {
|
||||
// create organization
|
||||
// this org is created as an additional org which should NOT
|
||||
// be returned in the results of this test case
|
||||
@@ -720,7 +759,7 @@ func TestListOrganization(t *testing.T) {
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
err = organizationRepo.Create(ctx, &org)
|
||||
err = organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
noOfOrganizations := 5
|
||||
@@ -728,14 +767,14 @@ func TestListOrganization(t *testing.T) {
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
org := domain.Organization{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateInactive,
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -743,13 +782,16 @@ func TestListOrganization(t *testing.T) {
|
||||
|
||||
return organizations
|
||||
},
|
||||
conditionClauses: []database.Condition{organizationRepo.StateCondition(domain.OrgStateInactive)},
|
||||
conditionClauses: []database.Condition{
|
||||
organizationRepo.InstanceIDCondition(instanceId),
|
||||
organizationRepo.StateCondition(domain.OrgStateInactive),
|
||||
},
|
||||
},
|
||||
func() test {
|
||||
instanceId_2 := gofakeit.Name()
|
||||
return test{
|
||||
name: "multiple organization filter on instance",
|
||||
testFunc: func(ctx context.Context, t *testing.T) []*domain.Organization {
|
||||
testFunc: func(t *testing.T, tx database.QueryExecutor) []*domain.Organization {
|
||||
// create instance 1
|
||||
instanceId_1 := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -761,8 +803,7 @@ func TestListOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err = instanceRepo.Create(ctx, &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// create organization
|
||||
@@ -774,7 +815,7 @@ func TestListOrganization(t *testing.T) {
|
||||
InstanceID: instanceId_1,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
err = organizationRepo.Create(ctx, &org)
|
||||
err = organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create instance 2
|
||||
@@ -787,7 +828,7 @@ func TestListOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
err = instanceRepo.Create(ctx, &instance_2)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance_2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
noOfOrganizations := 5
|
||||
@@ -795,14 +836,14 @@ func TestListOrganization(t *testing.T) {
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
org := domain.Organization{
|
||||
ID: gofakeit.Name(),
|
||||
ID: strconv.Itoa(i),
|
||||
Name: gofakeit.Name(),
|
||||
InstanceID: instanceId_2,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -816,22 +857,25 @@ func TestListOrganization(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := pool.Exec(ctx, "DELETE FROM zitadel.organizations")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
savepoint, err := tx.Begin(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = savepoint.Rollback(t.Context())
|
||||
if err != nil {
|
||||
t.Logf("error during rollback: %v", err)
|
||||
}
|
||||
}()
|
||||
organizations := tt.testFunc(t, savepoint)
|
||||
|
||||
organizations := tt.testFunc(ctx, t)
|
||||
|
||||
var condition database.Condition
|
||||
condition := organizationRepo.InstanceIDCondition(instanceId)
|
||||
if len(tt.conditionClauses) > 0 {
|
||||
condition = database.And(tt.conditionClauses...)
|
||||
}
|
||||
|
||||
// check organization values
|
||||
returnedOrgs, err := organizationRepo.List(ctx,
|
||||
returnedOrgs, err := organizationRepo.List(t.Context(), tx,
|
||||
database.WithCondition(condition),
|
||||
database.WithOrderByAscending(organizationRepo.CreatedAtColumn()),
|
||||
database.WithOrderByAscending(organizationRepo.CreatedAtColumn(), organizationRepo.IDColumn()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
if tt.noOrganizationReturned {
|
||||
@@ -851,6 +895,15 @@ func TestListOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteOrganization(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = tx.Rollback(t.Context())
|
||||
})
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
organizationRepo := repository.OrganizationRepository()
|
||||
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
instance := domain.Instance{
|
||||
@@ -862,24 +915,22 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
}
|
||||
instanceRepo := repository.InstanceRepository(pool)
|
||||
err := instanceRepo.Create(t.Context(), &instance)
|
||||
err = instanceRepo.Create(t.Context(), tx, &instance)
|
||||
require.NoError(t, err)
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
testFunc func(ctx context.Context, t *testing.T)
|
||||
orgIdentifierCondition domain.OrgIdentifierCondition
|
||||
testFunc func(t *testing.T)
|
||||
orgIdentifierCondition database.Condition
|
||||
noOfDeletedRows int64
|
||||
}
|
||||
tests := []test{
|
||||
func() test {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
organizationId := gofakeit.Name()
|
||||
var noOfOrganizations int64 = 1
|
||||
return test{
|
||||
name: "happy path delete organization filter id",
|
||||
testFunc: func(ctx context.Context, t *testing.T) {
|
||||
testFunc: func(t *testing.T) {
|
||||
organizations := make([]*domain.Organization, noOfOrganizations)
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
@@ -891,7 +942,7 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
@@ -902,12 +953,11 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
organizationName := gofakeit.Name()
|
||||
var noOfOrganizations int64 = 1
|
||||
return test{
|
||||
name: "happy path delete organization filter name",
|
||||
testFunc: func(ctx context.Context, t *testing.T) {
|
||||
testFunc: func(t *testing.T) {
|
||||
organizations := make([]*domain.Organization, noOfOrganizations)
|
||||
for i := range noOfOrganizations {
|
||||
|
||||
@@ -919,30 +969,28 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
}
|
||||
},
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(organizationName),
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(database.TextOperationEqual, organizationName),
|
||||
noOfDeletedRows: noOfOrganizations,
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
non_existent_organization_name := gofakeit.Name()
|
||||
return test{
|
||||
name: "delete non existent organization",
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(non_existent_organization_name),
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(database.TextOperationEqual, non_existent_organization_name),
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
organizationName := gofakeit.Name()
|
||||
return test{
|
||||
name: "deleted already deleted organization",
|
||||
testFunc: func(ctx context.Context, t *testing.T) {
|
||||
testFunc: func(t *testing.T) {
|
||||
noOfOrganizations := 1
|
||||
organizations := make([]*domain.Organization, noOfOrganizations)
|
||||
for i := range noOfOrganizations {
|
||||
@@ -955,21 +1003,23 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
// create organization
|
||||
err := organizationRepo.Create(ctx, &org)
|
||||
err := organizationRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
organizations[i] = &org
|
||||
}
|
||||
|
||||
// delete organization
|
||||
affectedRows, err := organizationRepo.Delete(ctx,
|
||||
organizationRepo.NameCondition(organizationName),
|
||||
organizations[0].InstanceID,
|
||||
affectedRows, err := organizationRepo.Delete(t.Context(), tx,
|
||||
database.And(
|
||||
organizationRepo.InstanceIDCondition(organizations[0].InstanceID),
|
||||
organizationRepo.NameCondition(database.TextOperationEqual, organizationName),
|
||||
),
|
||||
)
|
||||
assert.Equal(t, int64(1), affectedRows)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(organizationName),
|
||||
orgIdentifierCondition: organizationRepo.NameCondition(database.TextOperationEqual, organizationName),
|
||||
// this test should return 0 affected rows as the org was already deleted
|
||||
noOfDeletedRows: 0,
|
||||
}
|
||||
@@ -977,23 +1027,22 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
organizationRepo := repository.OrganizationRepository(pool)
|
||||
|
||||
if tt.testFunc != nil {
|
||||
tt.testFunc(ctx, t)
|
||||
tt.testFunc(t)
|
||||
}
|
||||
|
||||
// delete organization
|
||||
noOfDeletedRows, err := organizationRepo.Delete(ctx,
|
||||
tt.orgIdentifierCondition,
|
||||
instanceId,
|
||||
noOfDeletedRows, err := organizationRepo.Delete(t.Context(), tx,
|
||||
database.And(
|
||||
organizationRepo.InstanceIDCondition(instanceId),
|
||||
tt.orgIdentifierCondition,
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, noOfDeletedRows, tt.noOfDeletedRows)
|
||||
|
||||
// check organization was deleted
|
||||
organization, err := organizationRepo.Get(ctx,
|
||||
organization, err := organizationRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
tt.orgIdentifierCondition,
|
||||
@@ -1006,3 +1055,99 @@ func TestDeleteOrganization(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrganizationWithSubResources(t *testing.T) {
|
||||
tx, err := pool.Begin(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = tx.Rollback(t.Context())
|
||||
})
|
||||
|
||||
instanceRepo := repository.InstanceRepository()
|
||||
orgRepo := repository.OrganizationRepository()
|
||||
|
||||
// create instance
|
||||
instanceId := gofakeit.Name()
|
||||
err = instanceRepo.Create(t.Context(), tx, &domain.Instance{
|
||||
ID: instanceId,
|
||||
Name: gofakeit.Name(),
|
||||
DefaultOrgID: "defaultOrgId",
|
||||
IAMProjectID: "iamProject",
|
||||
ConsoleClientID: "consoleCLient",
|
||||
ConsoleAppID: "consoleApp",
|
||||
DefaultLanguage: "defaultLanguage",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create organization
|
||||
org := domain.Organization{
|
||||
ID: "1",
|
||||
Name: "org-name",
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
}
|
||||
err = orgRepo.Create(t.Context(), tx, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = orgRepo.Create(t.Context(), tx, &domain.Organization{
|
||||
ID: "without-sub-resources",
|
||||
Name: "org-name-2",
|
||||
InstanceID: instanceId,
|
||||
State: domain.OrgStateActive,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("domains", func(t *testing.T) {
|
||||
domainRepo := repository.OrganizationDomainRepository()
|
||||
|
||||
domain1 := &domain.AddOrganizationDomain{
|
||||
InstanceID: org.InstanceID,
|
||||
OrgID: org.ID,
|
||||
Domain: "domain1.com",
|
||||
IsVerified: true,
|
||||
IsPrimary: true,
|
||||
}
|
||||
err = domainRepo.Add(t.Context(), tx, domain1)
|
||||
require.NoError(t, err)
|
||||
|
||||
domain2 := &domain.AddOrganizationDomain{
|
||||
InstanceID: org.InstanceID,
|
||||
OrgID: org.ID,
|
||||
Domain: "domain2.com",
|
||||
IsVerified: false,
|
||||
IsPrimary: false,
|
||||
}
|
||||
err = domainRepo.Add(t.Context(), tx, domain2)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("org by domain", func(t *testing.T) {
|
||||
orgRepo := orgRepo.LoadDomains()
|
||||
|
||||
returnedOrg, err := orgRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
orgRepo.InstanceIDCondition(instanceId),
|
||||
orgRepo.ExistsDomain(domainRepo.DomainCondition(database.TextOperationEqual, domain1.Domain)),
|
||||
),
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, org.ID, returnedOrg.ID)
|
||||
assert.Len(t, returnedOrg.Domains, 2)
|
||||
})
|
||||
|
||||
t.Run("ensure org by domain works without LoadDomains", func(t *testing.T) {
|
||||
returnedOrg, err := orgRepo.Get(t.Context(), tx,
|
||||
database.WithCondition(
|
||||
database.And(
|
||||
orgRepo.InstanceIDCondition(instanceId),
|
||||
orgRepo.ExistsDomain(domainRepo.DomainCondition(database.TextOperationEqual, domain1.Domain)),
|
||||
),
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, org.ID, returnedOrg.ID)
|
||||
assert.Len(t, returnedOrg.Domains, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
type repository struct {
|
||||
client database.QueryExecutor
|
||||
}
|
||||
|
||||
func writeCondition(
|
||||
builder *database.StatementBuilder,
|
||||
condition database.Condition,
|
||||
|
||||
@@ -12,16 +12,10 @@ const queryUserStmt = `SELECT instance_id, org_id, id, username, type, created_a
|
||||
` first_name, last_name, email_address, email_verified_at, phone_number, phone_verified_at, description` +
|
||||
` FROM users_view users`
|
||||
|
||||
type user struct {
|
||||
repository
|
||||
}
|
||||
type user struct{}
|
||||
|
||||
func UserRepository(client database.QueryExecutor) domain.UserRepository {
|
||||
return &user{
|
||||
repository: repository{
|
||||
client: client,
|
||||
},
|
||||
}
|
||||
func UserRepository() domain.UserRepository {
|
||||
return new(user)
|
||||
}
|
||||
|
||||
var _ domain.UserRepository = (*user)(nil)
|
||||
@@ -31,17 +25,17 @@ var _ domain.UserRepository = (*user)(nil)
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// Human implements [domain.UserRepository].
|
||||
func (u *user) Human() domain.HumanRepository {
|
||||
func (u user) Human() domain.HumanRepository {
|
||||
return &userHuman{user: u}
|
||||
}
|
||||
|
||||
// Machine implements [domain.UserRepository].
|
||||
func (u *user) Machine() domain.MachineRepository {
|
||||
func (u user) Machine() domain.MachineRepository {
|
||||
return &userMachine{user: u}
|
||||
}
|
||||
|
||||
// List implements [domain.UserRepository].
|
||||
func (u *user) List(ctx context.Context, opts ...database.QueryOption) (users []*domain.User, err error) {
|
||||
func (u user) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (users []*domain.User, err error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
@@ -54,7 +48,7 @@ func (u *user) List(ctx context.Context, opts ...database.QueryOption) (users []
|
||||
options.WriteLimit(&builder)
|
||||
options.WriteOffset(&builder)
|
||||
|
||||
rows, err := u.client.Query(ctx, builder.String(), builder.Args()...)
|
||||
rows, err := client.Query(ctx, builder.String(), builder.Args()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,7 +73,7 @@ func (u *user) List(ctx context.Context, opts ...database.QueryOption) (users []
|
||||
}
|
||||
|
||||
// Get implements [domain.UserRepository].
|
||||
func (u *user) Get(ctx context.Context, opts ...database.QueryOption) (*domain.User, error) {
|
||||
func (u user) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.User, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
@@ -92,7 +86,7 @@ func (u *user) Get(ctx context.Context, opts ...database.QueryOption) (*domain.U
|
||||
options.WriteLimit(&builder)
|
||||
options.WriteOffset(&builder)
|
||||
|
||||
return scanUser(u.client.QueryRow(ctx, builder.String(), builder.Args()...))
|
||||
return scanUser(client.QueryRow(ctx, builder.String(), builder.Args()...))
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -105,7 +99,7 @@ const (
|
||||
)
|
||||
|
||||
// Create implements [domain.UserRepository].
|
||||
func (u *user) Create(ctx context.Context, user *domain.User) error {
|
||||
func (u user) Create(ctx context.Context, client database.QueryExecutor, user *domain.User) error {
|
||||
builder := database.StatementBuilder{}
|
||||
builder.AppendArgs(user.InstanceID, user.OrgID, user.ID, user.Username, user.Traits.Type())
|
||||
switch trait := user.Traits.(type) {
|
||||
@@ -116,15 +110,15 @@ func (u *user) Create(ctx context.Context, user *domain.User) error {
|
||||
builder.WriteString(createMachineStmt)
|
||||
builder.AppendArgs(trait.Description)
|
||||
}
|
||||
return u.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&user.CreatedAt, &user.UpdatedAt)
|
||||
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&user.CreatedAt, &user.UpdatedAt)
|
||||
}
|
||||
|
||||
// Delete implements [domain.UserRepository].
|
||||
func (u *user) Delete(ctx context.Context, condition database.Condition) error {
|
||||
func (u user) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) error {
|
||||
builder := database.StatementBuilder{}
|
||||
builder.WriteString("DELETE FROM users")
|
||||
writeCondition(&builder, condition)
|
||||
_, err := u.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
_, err := client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// -------------------------------------------------------------
|
||||
|
||||
type userHuman struct {
|
||||
*user
|
||||
user
|
||||
}
|
||||
|
||||
var _ domain.HumanRepository = (*userHuman)(nil)
|
||||
@@ -21,14 +21,14 @@ var _ domain.HumanRepository = (*userHuman)(nil)
|
||||
const userEmailQuery = `SELECT h.email_address, h.email_verified_at FROM user_humans h`
|
||||
|
||||
// GetEmail implements [domain.HumanRepository].
|
||||
func (u *userHuman) GetEmail(ctx context.Context, condition database.Condition) (*domain.Email, error) {
|
||||
func (u *userHuman) GetEmail(ctx context.Context, client database.QueryExecutor, condition database.Condition) (*domain.Email, error) {
|
||||
var email domain.Email
|
||||
|
||||
builder := database.StatementBuilder{}
|
||||
builder.WriteString(userEmailQuery)
|
||||
writeCondition(&builder, condition)
|
||||
|
||||
err := u.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(
|
||||
err := client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(
|
||||
&email.Address,
|
||||
&email.VerifiedAt,
|
||||
)
|
||||
@@ -39,7 +39,7 @@ func (u *userHuman) GetEmail(ctx context.Context, condition database.Condition)
|
||||
}
|
||||
|
||||
// Update implements [domain.HumanRepository].
|
||||
func (h userHuman) Update(ctx context.Context, condition database.Condition, changes ...database.Change) error {
|
||||
func (h userHuman) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) error {
|
||||
builder := database.StatementBuilder{}
|
||||
builder.WriteString(`UPDATE human_users SET `)
|
||||
database.Changes(changes).Write(&builder)
|
||||
@@ -47,7 +47,7 @@ func (h userHuman) Update(ctx context.Context, condition database.Condition, cha
|
||||
|
||||
stmt := builder.String()
|
||||
|
||||
_, err := h.client.Exec(ctx, stmt, builder.Args()...)
|
||||
_, err := client.Exec(ctx, stmt, builder.Args()...)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type userMachine struct {
|
||||
*user
|
||||
user
|
||||
}
|
||||
|
||||
var _ domain.MachineRepository = (*userMachine)(nil)
|
||||
@@ -18,14 +18,14 @@ var _ domain.MachineRepository = (*userMachine)(nil)
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// Update implements [domain.MachineRepository].
|
||||
func (m userMachine) Update(ctx context.Context, condition database.Condition, changes ...database.Change) error {
|
||||
func (m userMachine) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) error {
|
||||
builder := database.StatementBuilder{}
|
||||
builder.WriteString("UPDATE user_machines SET ")
|
||||
database.Changes(changes).Write(&builder)
|
||||
writeCondition(&builder, condition)
|
||||
m.writeReturning()
|
||||
|
||||
_, err := m.client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
_, err := client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user