fixup! fixup! Merge branch 'main' into import_export_merge

This commit is contained in:
Iraq Jaber
2025-06-05 10:10:14 +02:00
parent b0171c83df
commit 5b1099c90f
15 changed files with 478 additions and 111 deletions

View File

@@ -9,16 +9,16 @@ import (
) )
type Instance struct { type Instance struct {
ID string `json:"id"` ID string `json:"id,omitempty" db:"id"`
Name string `json:"name"` Name string `json:"name,omitempty" db:"name"`
DefaultOrgID string `json:"default_org_id"` DefaultOrgID string `json:"default_org_id,omitempty" db:"default_org_id"`
IAMProjectID string `json:"iam_project_id"` IAMProjectID string `json:"iam_project_id,omitempty" db:"iam_project_id"`
ConsoleClientId string `json:"console_client_id"` ConsoleClientID string `json:"console_client_id,omitempty" db:"console_client_id"`
ConsoleAppID string `json:"console_app_id"` ConsoleAppID string `json:"console_app_id,omitempty" db:"console_app_id"`
DefaultLanguage string `json:"default_language"` DefaultLanguage string `json:"default_language,omitempty" db:"default_language"`
CreatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-,omitempty" db:"created_at"`
UpdatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-,omitempty" db:"updated_at"`
DeletedAt *time.Time `json:"-"` DeletedAt *time.Time `json:"-,omitempty" db:"deleted_at"`
} }
type instanceCacheIndex uint8 type instanceCacheIndex uint8
@@ -44,8 +44,8 @@ type instanceColumns interface {
IDColumn() database.Column IDColumn() database.Column
// NameColumn returns the column for the name field. // NameColumn returns the column for the name field.
NameColumn() database.Column NameColumn() database.Column
// DefaultOrgIdColumn returns the column for the default org id field // DefaultOrgIDColumn returns the column for the default org id field
DefaultOrgIdColumn() database.Column DefaultOrgIDColumn() database.Column
// IAMProjectIDColumn returns the column for the default IAM org id field // IAMProjectIDColumn returns the column for the default IAM org id field
IAMProjectIDColumn() database.Column IAMProjectIDColumn() database.Column
// ConsoleClientIDColumn returns the column for the default IAM org id field // ConsoleClientIDColumn returns the column for the default IAM org id field
@@ -87,9 +87,10 @@ type InstanceRepository interface {
// Member() MemberRepository // Member() MemberRepository
Get(ctx context.Context, opts ...database.Condition) (*Instance, error) Get(ctx context.Context, opts ...database.Condition) (*Instance, error)
List(ctx context.Context, opts ...database.Condition) ([]*Instance, error)
Create(ctx context.Context, instance *Instance) error Create(ctx context.Context, instance *Instance) error
Update(ctx context.Context, condition database.Condition, changes ...database.Change) error Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error)
Delete(ctx context.Context, condition database.Condition) error Delete(ctx context.Context, condition database.Condition) error
} }

View File

@@ -31,7 +31,7 @@ type Querier interface {
// Executor is a database client that can execute statements. // Executor is a database client that can execute statements.
type Executor interface { type Executor interface {
Exec(ctx context.Context, stmt string, args ...any) error Exec(ctx context.Context, stmt string, args ...any) (int64, error)
} }
// QueryExecutor is a database client that can execute queries and statements. // QueryExecutor is a database client that can execute queries and statements.

View File

@@ -13,9 +13,7 @@ type pgxConn struct {
*pgxpool.Conn *pgxpool.Conn
} }
var ( var _ database.Client = (*pgxConn)(nil)
_ database.Client = (*pgxConn)(nil)
)
// Release implements [database.Client]. // Release implements [database.Client].
func (c *pgxConn) Release(_ context.Context) error { func (c *pgxConn) Release(_ context.Context) error {
@@ -47,9 +45,9 @@ func (c *pgxConn) QueryRow(ctx context.Context, sql string, args ...any) databas
// Exec implements [database.Pool]. // Exec implements [database.Pool].
// Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool. // Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool.
func (c *pgxConn) Exec(ctx context.Context, sql string, args ...any) error { func (c *pgxConn) Exec(ctx context.Context, sql string, args ...any) (int64, error) {
_, err := c.Conn.Exec(ctx, sql, args...) res, err := c.Conn.Exec(ctx, sql, args...)
return err return res.RowsAffected(), err
} }
// Migrate implements [database.Migrator]. // Migrate implements [database.Migrator].

View File

@@ -1,6 +1,6 @@
CREATE TABLE IF NOT EXISTS zitadel.instances( CREATE TABLE IF NOT EXISTS zitadel.instances(
id TEXT NOT NULL PRIMARY KEY, id TEXT NOT NULL CHECK (id <> '') PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL CHECK (name <> ''),
default_org_id TEXT, -- NOT NULL, default_org_id TEXT, -- NOT NULL,
iam_project_id TEXT, -- NOT NULL, iam_project_id TEXT, -- NOT NULL,
console_client_id TEXT, -- NOT NULL, console_client_id TEXT, -- NOT NULL,

View File

@@ -45,9 +45,9 @@ func (c *pgxPool) QueryRow(ctx context.Context, sql string, args ...any) databas
// Exec implements [database.Pool]. // Exec implements [database.Pool].
// Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool. // Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool.
func (c *pgxPool) Exec(ctx context.Context, sql string, args ...any) error { func (c *pgxPool) Exec(ctx context.Context, sql string, args ...any) (int64, error) {
_, err := c.Pool.Exec(ctx, sql, args...) res, err := c.Pool.Exec(ctx, sql, args...)
return err return res.RowsAffected(), err
} }
// Begin implements [database.Pool]. // Begin implements [database.Pool].

View File

@@ -46,9 +46,9 @@ func (tx *pgxTx) QueryRow(ctx context.Context, sql string, args ...any) database
// Exec implements [database.Transaction]. // Exec implements [database.Transaction].
// Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool. // Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool.
func (tx *pgxTx) Exec(ctx context.Context, sql string, args ...any) error { func (tx *pgxTx) Exec(ctx context.Context, sql string, args ...any) (int64, error) {
_, err := tx.Tx.Exec(ctx, sql, args...) res, err := tx.Tx.Exec(ctx, sql, args...)
return err return res.RowsAffected(), err
} }
// Begin implements [database.Transaction]. // Begin implements [database.Transaction].

View File

@@ -5,6 +5,7 @@ import (
"errors" "errors"
"time" "time"
"github.com/jackc/pgx/v5/pgconn"
"github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database"
) )
@@ -37,14 +38,33 @@ func (i *instance) Get(ctx context.Context, opts ...database.Condition) (*domain
i.builder.WriteString(queryInstanceStmt) i.builder.WriteString(queryInstanceStmt)
isNotDeletedCondition := database.IsNull(i.DeletedAtColumn()) opts = append(opts, database.IsNull(i.DeletedAtColumn()))
opts = append(opts, isNotDeletedCondition)
andCondition := database.And(opts...) andCondition := database.And(opts...)
andCondition.Write(&i.builder) andCondition.Write(&i.builder)
return scanInstance(i.client.QueryRow(ctx, i.builder.String(), i.builder.Args()...)) return scanInstance(i.client.QueryRow(ctx, i.builder.String(), i.builder.Args()...))
} }
// List implements [domain.InstanceRepository].
// func (i *instance) List(ctx context.Context, opts ...database.QueryOption) (*domain.Instance, error) {
func (i *instance) List(ctx context.Context, opts ...database.Condition) ([]*domain.Instance, error) {
i.builder = database.StatementBuilder{}
i.builder.WriteString(queryInstanceStmt)
opts = append(opts, database.IsNull(i.DeletedAtColumn()))
andCondition := database.And(opts...)
andCondition.Write(&i.builder)
rows, err := i.client.Query(ctx, i.builder.String(), i.builder.Args()...)
if err != nil {
return nil, err
}
defer rows.Close()
return scanInstances(rows)
}
const createInstanceStmt = `INSERT INTO zitadel.instances (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language)` + const createInstanceStmt = `INSERT INTO zitadel.instances (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language)` +
` VALUES ($1, $2, $3, $4, $5, $6, $7)` + ` VALUES ($1, $2, $3, $4, $5, $6, $7)` +
` RETURNING created_at, updated_at` ` RETURNING created_at, updated_at`
@@ -52,14 +72,35 @@ const createInstanceStmt = `INSERT INTO zitadel.instances (id, name, default_org
// Create implements [domain.InstanceRepository]. // Create implements [domain.InstanceRepository].
func (i *instance) Create(ctx context.Context, instance *domain.Instance) error { func (i *instance) Create(ctx context.Context, instance *domain.Instance) error {
i.builder = database.StatementBuilder{} i.builder = database.StatementBuilder{}
i.builder.AppendArgs(instance.ID, instance.Name, instance.DefaultOrgID, instance.IAMProjectID, instance.ConsoleClientId, instance.ConsoleAppID, instance.DefaultLanguage) i.builder.AppendArgs(instance.ID, instance.Name, instance.DefaultOrgID, instance.IAMProjectID, instance.ConsoleClientID, instance.ConsoleAppID, instance.DefaultLanguage)
i.builder.WriteString(createInstanceStmt) i.builder.WriteString(createInstanceStmt)
return i.client.QueryRow(ctx, i.builder.String(), i.builder.Args()...).Scan(&instance.CreatedAt, &instance.UpdatedAt) err := i.client.QueryRow(ctx, i.builder.String(), i.builder.Args()...).Scan(&instance.CreatedAt, &instance.UpdatedAt)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
// constraint violation
if pgErr.Code == "23514" {
if pgErr.ConstraintName == "instances_name_check" {
return errors.New("instnace name not provided")
}
if pgErr.ConstraintName == "instances_id_check" {
return errors.New("instnace id not provided")
}
}
// duplicate
if pgErr.Code == "23505" {
if pgErr.ConstraintName == "instances_pkey" {
return errors.New("instnace id already exists")
}
}
}
}
return err
} }
// Update implements [domain.InstanceRepository]. // Update implements [domain.InstanceRepository].
func (i instance) Update(ctx context.Context, condition database.Condition, changes ...database.Change) error { func (i instance) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (int64, error) {
i.builder = database.StatementBuilder{} i.builder = database.StatementBuilder{}
i.builder.WriteString(`UPDATE zitadel.instances SET `) i.builder.WriteString(`UPDATE zitadel.instances SET `)
database.Changes(changes).Write(&i.builder) database.Changes(changes).Write(&i.builder)
@@ -67,7 +108,8 @@ func (i instance) Update(ctx context.Context, condition database.Condition, chan
stmt := i.builder.String() stmt := i.builder.String()
return i.client.Exec(ctx, stmt, i.builder.Args()...) rowsAffected, err := i.client.Exec(ctx, stmt, i.builder.Args()...)
return rowsAffected, err
} }
// Delete implements [domain.InstanceRepository]. // Delete implements [domain.InstanceRepository].
@@ -80,7 +122,8 @@ func (i instance) Delete(ctx context.Context, condition database.Condition) erro
i.builder.AppendArgs(time.Now()) i.builder.AppendArgs(time.Now())
i.writeCondition(condition) i.writeCondition(condition)
return i.client.Exec(ctx, i.builder.String(), i.builder.Args()...) _, err := i.client.Exec(ctx, i.builder.String(), i.builder.Args()...)
return err
} }
// ------------------------------------------------------------- // -------------------------------------------------------------
@@ -126,7 +169,7 @@ func (instance) CreatedAtColumn() database.Column {
} }
// DefaultOrgIdColumn implements [domain.instanceColumns]. // DefaultOrgIdColumn implements [domain.instanceColumns].
func (instance) DefaultOrgIdColumn() database.Column { func (instance) DefaultOrgIDColumn() database.Column {
return database.NewColumn("default_org_id") return database.NewColumn("default_org_id")
} }
@@ -175,7 +218,7 @@ func scanInstance(scanner database.Scanner) (*domain.Instance, error) {
&instance.Name, &instance.Name,
&instance.DefaultOrgID, &instance.DefaultOrgID,
&instance.IAMProjectID, &instance.IAMProjectID,
&instance.ConsoleClientId, &instance.ConsoleClientID,
&instance.ConsoleAppID, &instance.ConsoleAppID,
&instance.DefaultLanguage, &instance.DefaultLanguage,
&instance.CreatedAt, &instance.CreatedAt,
@@ -194,3 +237,30 @@ func scanInstance(scanner database.Scanner) (*domain.Instance, error) {
return &instance, nil return &instance, nil
} }
func scanInstances(rows database.Rows) ([]*domain.Instance, error) {
instances := make([]*domain.Instance, 0)
for rows.Next() {
var instance domain.Instance
err := rows.Scan(
&instance.ID,
&instance.Name,
&instance.DefaultOrgID,
&instance.IAMProjectID,
&instance.ConsoleClientID,
&instance.ConsoleAppID,
&instance.DefaultLanguage,
&instance.CreatedAt,
&instance.UpdatedAt,
&instance.DeletedAt,
)
if err != nil {
return nil, err
}
instances = append(instances, &instance)
}
return instances, nil
}

View File

@@ -2,12 +2,12 @@ package repository_test
import ( import (
"context" "context"
"errors"
"testing" "testing"
"time" "time"
"github.com/brianvoe/gofakeit/v6" "github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database"
@@ -15,6 +15,50 @@ import (
) )
func TestCreateInstance(t *testing.T) { func TestCreateInstance(t *testing.T) {
tests := []struct {
name string
testFunc func() *domain.Instance
instance domain.Instance
err error
}{
{
name: "happy path",
instance: func() domain.Instance {
instanceId := gofakeit.Name()
instanceName := gofakeit.Name()
instance := domain.Instance{
ID: instanceId,
Name: instanceName,
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
return instance
}(),
},
{
name: "create instance wihtout name",
instance: func() domain.Instance {
instanceId := gofakeit.Name()
// instanceName := gofakeit.Name()
instance := domain.Instance{
ID: instanceId,
Name: "",
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
return instance
}(),
err: errors.New("instnace name not provided"),
},
{
name: "adding same instance twice",
testFunc: func() *domain.Instance {
instanceRepo := repository.InstanceRepository(pool) instanceRepo := repository.InstanceRepository(pool)
instanceId := gofakeit.Name() instanceId := gofakeit.Name()
instanceName := gofakeit.Name() instanceName := gofakeit.Name()
@@ -25,33 +69,85 @@ func TestCreateInstance(t *testing.T) {
Name: instanceName, Name: instanceName,
DefaultOrgID: "defaultOrgId", DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject", IAMProjectID: "iamProject",
ConsoleClientId: "consoleCLient", ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp", ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage", DefaultLanguage: "defaultLanguage",
} }
beforeCreate := time.Now()
err := instanceRepo.Create(ctx, &inst) err := instanceRepo.Create(ctx, &inst)
require.NoError(t, err) assert.NoError(t, err)
return &inst
},
err: errors.New("instnace id already exists"),
},
{
name: "adding instance with no id",
instance: func() domain.Instance {
// instanceId := gofakeit.Name()
instanceName := gofakeit.Name()
instance := domain.Instance{
// ID: instanceId,
Name: instanceName,
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
return instance
}(),
err: errors.New("instnace id not provided"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
var instance *domain.Instance
if tt.testFunc != nil {
instance = tt.testFunc()
} else {
instance = &tt.instance
}
instanceRepo := repository.InstanceRepository(pool)
// create instance
beforeCreate := time.Now()
err := instanceRepo.Create(ctx, instance)
assert.Equal(t, tt.err, err)
if err != nil {
return
}
afterCreate := time.Now() afterCreate := time.Now()
instance, err := instanceRepo.Get(ctx, // check instance values
instanceRepo.NameCondition(database.TextOperationEqual, instanceName), instance, err = instanceRepo.Get(ctx,
instanceRepo.NameCondition(database.TextOperationEqual, instance.Name),
) )
require.Equal(t, inst.ID, instance.ID) assert.Equal(t, tt.instance.ID, instance.ID)
require.Equal(t, inst.Name, instance.Name) assert.Equal(t, tt.instance.Name, instance.Name)
require.Equal(t, inst.DefaultOrgID, instance.DefaultOrgID) assert.Equal(t, tt.instance.DefaultOrgID, instance.DefaultOrgID)
require.Equal(t, inst.IAMProjectID, instance.IAMProjectID) assert.Equal(t, tt.instance.IAMProjectID, instance.IAMProjectID)
require.Equal(t, inst.ConsoleClientId, instance.ConsoleClientId) assert.Equal(t, tt.instance.ConsoleClientID, instance.ConsoleClientID)
require.Equal(t, inst.ConsoleAppID, instance.ConsoleAppID) assert.Equal(t, tt.instance.ConsoleAppID, instance.ConsoleAppID)
require.Equal(t, inst.DefaultLanguage, instance.DefaultLanguage) assert.Equal(t, tt.instance.DefaultLanguage, instance.DefaultLanguage)
assert.WithinRange(t, instance.CreatedAt, beforeCreate, afterCreate) assert.WithinRange(t, instance.CreatedAt, beforeCreate, afterCreate)
assert.WithinRange(t, instance.UpdatedAt, beforeCreate, afterCreate) assert.WithinRange(t, instance.UpdatedAt, beforeCreate, afterCreate)
require.Nil(t, instance.DeletedAt) assert.Nil(t, instance.DeletedAt)
require.NoError(t, err) assert.NoError(t, err)
})
}
} }
func TestUpdateNameInstance(t *testing.T) { func TestUpdateNameInstance(t *testing.T) {
tests := []struct {
name string
testFunc func() *domain.Instance
rowsAffected int64
}{
{
name: "happy path",
testFunc: func() *domain.Instance {
instanceRepo := repository.InstanceRepository(pool) instanceRepo := repository.InstanceRepository(pool)
instanceId := gofakeit.Name() instanceId := gofakeit.Name()
instanceName := gofakeit.Name() instanceName := gofakeit.Name()
@@ -62,34 +158,77 @@ func TestUpdateNameInstance(t *testing.T) {
Name: instanceName, Name: instanceName,
DefaultOrgID: "defaultOrgId", DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject", IAMProjectID: "iamProject",
ConsoleClientId: "consoleCLient", ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp", ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage", DefaultLanguage: "defaultLanguage",
} }
// create instance
err := instanceRepo.Create(ctx, &inst) err := instanceRepo.Create(ctx, &inst)
require.NoError(t, err) assert.NoError(t, err)
return &inst
},
rowsAffected: 1,
},
{
name: "update non existent instance",
testFunc: func() *domain.Instance {
instanceId := gofakeit.Name()
_, err = instanceRepo.Get(ctx, inst := domain.Instance{
instanceRepo.NameCondition(database.TextOperationEqual, instanceName), ID: instanceId,
) }
require.NoError(t, err) return &inst
},
rowsAffected: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
beforeUpdate := time.Now()
ctx := context.Background()
instanceRepo := repository.InstanceRepository(pool)
instance := tt.testFunc()
// update name // update name
err = instanceRepo.Update(ctx, newName := "new_" + instance.Name
instanceRepo.IDCondition(instanceId), rowsAffected, err := instanceRepo.Update(ctx,
instanceRepo.SetName("new_name"), instanceRepo.IDCondition(instance.ID),
instanceRepo.SetName(newName),
) )
require.NoError(t, err) afterUpdate := time.Now()
assert.NoError(t, err)
instance, err := instanceRepo.Get(ctx, assert.Equal(t, tt.rowsAffected, rowsAffected)
instanceRepo.IDCondition(instanceId),
) if rowsAffected == 0 {
require.NoError(t, err) return
require.Equal(t, "new_name", instance.Name)
} }
func TestUpdeDeleteInstance(t *testing.T) { // check instance values
instance, err = instanceRepo.Get(ctx,
instanceRepo.IDCondition(instance.ID),
)
assert.NoError(t, err)
assert.Equal(t, newName, instance.Name)
assert.WithinRange(t, instance.UpdatedAt, beforeUpdate, afterUpdate)
assert.Nil(t, instance.DeletedAt)
})
}
}
func TestGetInstance(t *testing.T) {
tests := []struct {
name string
testFunc func() *domain.Instance
noInstanceReturned bool
}{
{
name: "happy path",
testFunc: func() *domain.Instance {
instanceRepo := repository.InstanceRepository(pool) instanceRepo := repository.InstanceRepository(pool)
instanceId := gofakeit.Name() instanceId := gofakeit.Name()
instanceName := gofakeit.Name() instanceName := gofakeit.Name()
@@ -100,29 +239,172 @@ func TestUpdeDeleteInstance(t *testing.T) {
Name: instanceName, Name: instanceName,
DefaultOrgID: "defaultOrgId", DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject", IAMProjectID: "iamProject",
ConsoleClientId: "consoleCLient", ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
// create instance
err := instanceRepo.Create(ctx, &inst)
assert.NoError(t, err)
return &inst
},
},
{
name: "get non existent instance",
testFunc: func() *domain.Instance {
instanceId := gofakeit.Name()
inst := domain.Instance{
ID: instanceId,
}
return &inst
},
noInstanceReturned: true,
},
}
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()
}
// check instance values
returnedInstance, err := instanceRepo.Get(ctx,
instanceRepo.IDCondition(instance.ID),
)
assert.NoError(t, err)
if tt.noInstanceReturned {
assert.Nil(t, returnedInstance)
return
}
assert.Equal(t, returnedInstance.ID, instance.ID)
assert.Equal(t, returnedInstance.Name, instance.Name)
assert.Equal(t, returnedInstance.DefaultOrgID, instance.DefaultOrgID)
assert.Equal(t, returnedInstance.IAMProjectID, instance.IAMProjectID)
assert.Equal(t, returnedInstance.ConsoleClientID, instance.ConsoleClientID)
assert.Equal(t, returnedInstance.ConsoleAppID, instance.ConsoleAppID)
assert.Equal(t, returnedInstance.DefaultLanguage, instance.DefaultLanguage)
assert.NoError(t, err)
})
}
}
func TestListInstance(t *testing.T) {
tests := []struct {
name string
testFunc func() *domain.Instance
noInstanceReturned bool
}{
{
name: "happy path",
testFunc: func() *domain.Instance {
instanceRepo := repository.InstanceRepository(pool)
instanceId := gofakeit.Name()
instanceName := gofakeit.Name()
ctx := context.Background()
inst := domain.Instance{
ID: instanceId,
Name: instanceName,
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
// create instance
err := instanceRepo.Create(ctx, &inst)
assert.NoError(t, err)
return &inst
},
},
{
name: "get non existent instance",
testFunc: func() *domain.Instance {
instanceId := gofakeit.Name()
inst := domain.Instance{
ID: instanceId,
}
return &inst
},
noInstanceReturned: true,
},
}
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()
}
// check instance values
returnedInstance, err := instanceRepo.List(ctx,
instanceRepo.IDCondition(instance.ID),
)
assert.NoError(t, err)
if tt.noInstanceReturned {
assert.Nil(t, returnedInstance)
return
}
assert.Equal(t, returnedInstance.ID, instance.ID)
assert.Equal(t, returnedInstance.Name, instance.Name)
assert.Equal(t, returnedInstance.DefaultOrgID, instance.DefaultOrgID)
assert.Equal(t, returnedInstance.IAMProjectID, instance.IAMProjectID)
assert.Equal(t, returnedInstance.ConsoleClientID, instance.ConsoleClientID)
assert.Equal(t, returnedInstance.ConsoleAppID, instance.ConsoleAppID)
assert.Equal(t, returnedInstance.DefaultLanguage, instance.DefaultLanguage)
assert.NoError(t, err)
})
}
}
func TestUpdateDeleteInstance(t *testing.T) {
instanceRepo := repository.InstanceRepository(pool)
instanceId := gofakeit.Name()
instanceName := gofakeit.Name()
ctx := context.Background()
inst := domain.Instance{
ID: instanceId,
Name: instanceName,
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp", ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage", DefaultLanguage: "defaultLanguage",
} }
err := instanceRepo.Create(ctx, &inst) err := instanceRepo.Create(ctx, &inst)
require.NoError(t, err) assert.NoError(t, err)
instance, err := instanceRepo.Get(ctx, instance, err := instanceRepo.Get(ctx,
instanceRepo.NameCondition(database.TextOperationEqual, instanceName), instanceRepo.NameCondition(database.TextOperationEqual, instanceName),
) )
require.NotNil(t, instance) assert.NotNil(t, instance)
require.NoError(t, err) assert.NoError(t, err)
// delete instance // delete instance
err = instanceRepo.Delete(ctx, err = instanceRepo.Delete(ctx,
instanceRepo.IDCondition(instanceId), instanceRepo.IDCondition(instanceId),
) )
require.NoError(t, err) assert.NoError(t, err)
instance, err = instanceRepo.Get(ctx, instance, err = instanceRepo.Get(ctx,
instanceRepo.NameCondition(database.TextOperationEqual, instanceName), instanceRepo.NameCondition(database.TextOperationEqual, instanceName),
) )
require.NoError(t, err) assert.NoError(t, err)
require.Nil(t, instance) assert.Nil(t, instance)
} }

View File

@@ -3,7 +3,6 @@ package repository
import "github.com/zitadel/zitadel/backend/v3/storage/database" import "github.com/zitadel/zitadel/backend/v3/storage/database"
type repository struct { type repository struct {
// we can't reuse builder after it's been used already, I think we should remove it
builder database.StatementBuilder builder database.StatementBuilder
client database.QueryExecutor client database.QueryExecutor
} }

View File

@@ -120,7 +120,8 @@ func (u *user) Create(ctx context.Context, user *domain.User) error {
func (u *user) Delete(ctx context.Context, condition database.Condition) error { func (u *user) Delete(ctx context.Context, condition database.Condition) error {
u.builder.WriteString("DELETE FROM users") u.builder.WriteString("DELETE FROM users")
u.writeCondition(condition) u.writeCondition(condition)
return u.client.Exec(ctx, u.builder.String(), u.builder.Args()...) _, err := u.client.Exec(ctx, u.builder.String(), u.builder.Args()...)
return err
} }
// ------------------------------------------------------------- // -------------------------------------------------------------

View File

@@ -46,7 +46,8 @@ func (h userHuman) Update(ctx context.Context, condition database.Condition, cha
stmt := h.builder.String() stmt := h.builder.String()
return h.client.Exec(ctx, stmt, h.builder.Args()...) _, err := h.client.Exec(ctx, stmt, h.builder.Args()...)
return err
} }
// ------------------------------------------------------------- // -------------------------------------------------------------

View File

@@ -18,13 +18,14 @@ var _ domain.MachineRepository = (*userMachine)(nil)
// ------------------------------------------------------------- // -------------------------------------------------------------
// Update implements [domain.MachineRepository]. // Update implements [domain.MachineRepository].
func (m userMachine) Update(ctx context.Context, condition database.Condition, changes ...database.Change) (err error) { func (m userMachine) Update(ctx context.Context, condition database.Condition, changes ...database.Change) error {
m.builder.WriteString("UPDATE user_machines SET ") m.builder.WriteString("UPDATE user_machines SET ")
database.Changes(changes).Write(&m.builder) database.Changes(changes).Write(&m.builder)
m.writeCondition(condition) m.writeCondition(condition)
m.writeReturning() m.writeReturning()
return m.client.Exec(ctx, m.builder.String(), m.builder.Args()...) _, err := m.client.Exec(ctx, m.builder.String(), m.builder.Args()...)
return err
} }
// ------------------------------------------------------------- // -------------------------------------------------------------

View File

@@ -15,7 +15,8 @@ type Event struct {
func Publish(ctx context.Context, events []*Event, db database.Executor) error { func Publish(ctx context.Context, events []*Event, db database.Executor) error {
for _, event := range events { for _, event := range events {
if err := db.Exec(ctx, `INSERT INTO events (aggregate_type, aggregate_id) VALUES ($1, $2)`, event.AggregateType, event.AggregateID); err != nil { _, err := db.Exec(ctx, `INSERT INTO events (aggregate_type, aggregate_id) VALUES ($1, $2)`, event.AggregateType, event.AggregateID)
if err != nil {
return err return err
} }
} }

4
go.mod
View File

@@ -43,6 +43,7 @@ require (
github.com/h2non/gock v1.2.0 github.com/h2non/gock v1.2.0
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/improbable-eng/grpc-web v0.15.0 github.com/improbable-eng/grpc-web v0.15.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgx/v5 v5.7.3 github.com/jackc/pgx/v5 v5.7.3
github.com/jackc/tern/v2 v2.3.3 github.com/jackc/tern/v2 v2.3.3
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52
@@ -135,6 +136,9 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/lib/pq v1.10.9 // indirect github.com/lib/pq v1.10.9 // indirect

9
go.sum
View File

@@ -449,10 +449,19 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.3 h1:PO1wNKj/bTAwxSJnO1Z4Ai8j4magtqg2SLNjEDzcXQo= github.com/jackc/pgx/v5 v5.7.3 h1:PO1wNKj/bTAwxSJnO1Z4Ai8j4magtqg2SLNjEDzcXQo=