instance custom domain event tests done

This commit is contained in:
adlerhurst
2025-07-28 09:10:33 +02:00
parent c7718aca8f
commit ce60693c24
14 changed files with 565 additions and 278 deletions

View File

@@ -13,6 +13,13 @@ const (
DomainValidationTypeHTTP DomainValidationType = "http"
)
type DomainType string
const (
DomainTypeCustom DomainType = "custom"
DomainTypeTrusted DomainType = "trusted"
)
type domainColumns interface {
// InstanceIDColumn returns the column for the instance id field.
// `qualified` indicates if the column should be qualified with the table name.
@@ -20,15 +27,9 @@ type domainColumns interface {
// DomainColumn returns the column for the domain field.
// `qualified` indicates if the column should be qualified with the table name.
DomainColumn(qualified bool) database.Column
// IsVerifiedColumn returns the column for the is verified field.
// `qualified` indicates if the column should be qualified with the table name.
IsVerifiedColumn(qualified bool) database.Column
// IsPrimaryColumn returns the column for the is primary field.
// `qualified` indicates if the column should be qualified with the table name.
IsPrimaryColumn(qualified bool) database.Column
// ValidationTypeColumn returns the column for the verification type field.
// `qualified` indicates if the column should be qualified with the table name.
ValidationTypeColumn(qualified bool) database.Column
// CreatedAtColumn returns the column for the created at field.
// `qualified` indicates if the column should be qualified with the table name.
CreatedAtColumn(qualified bool) database.Column
@@ -44,13 +45,9 @@ type domainConditions interface {
DomainCondition(op database.TextOperation, domain string) database.Condition
// IsPrimaryCondition returns a filter on the is primary field.
IsPrimaryCondition(isPrimary bool) database.Condition
// IsVerifiedCondition returns a filter on the is verified field.
IsVerifiedCondition(isVerified bool) database.Condition
}
type domainChanges interface {
// SetVerified sets the is verified column to true.
SetVerified() database.Change
// SetPrimary sets a domain as primary based on the condition.
// All other domains will be set to non-primary.
//
@@ -62,9 +59,6 @@ type domainChanges interface {
// - The domain is already primary.
// - No domain matches the condition.
SetPrimary() database.Change
// SetValidationType sets the verification type column.
// If the domain is already verified, this is a no-op.
SetValidationType(verificationType DomainValidationType) database.Change
// SetUpdatedAt sets the updated at column.
// This is used for reducing events.
SetUpdatedAt(t time.Time) database.Change

View File

@@ -8,30 +8,28 @@ import (
)
type InstanceDomain struct {
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
Domain string `json:"domain,omitempty" db:"domain"`
IsVerified bool `json:"isVerified,omitempty" db:"is_verified"`
IsPrimary bool `json:"isPrimary,omitempty" db:"is_primary"`
ValidationType *DomainValidationType `json:"validationType,omitempty" db:"validation_type"`
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
Domain string `json:"domain,omitempty" db:"domain"`
IsPrimary bool `json:"isPrimary,omitempty" db:"is_primary"`
Type DomainType `json:"type,omitempty" db:"type"`
CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"`
}
type AddInstanceDomain struct {
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
Domain string `json:"domain,omitempty" db:"domain"`
IsVerified bool `json:"isVerified,omitempty" db:"is_verified"`
IsPrimary bool `json:"isPrimary,omitempty" db:"is_primary"`
IsGenerated bool `json:"isGenerated,omitempty" db:"is_generated"`
ValidationType *DomainValidationType `json:"validationType,omitempty" db:"validation_type"`
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
Domain string `json:"domain,omitempty" db:"domain"`
IsPrimary bool `json:"isPrimary,omitempty" db:"is_primary"`
IsGenerated bool `json:"isGenerated,omitempty" db:"is_generated"`
Type DomainType `json:"type,omitempty" db:"type"`
// CreatedAt is the time when the domain was added.
// It is set by the repository and should not be set by the caller.
CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"`
CreatedAt *time.Time `json:"createdAt,omitzero" db:"created_at"`
// UpdatedAt is the time when the domain was last updated.
// It is set by the repository and should not be set by the caller.
UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"`
UpdatedAt *time.Time `json:"updatedAt,omitzero" db:"updated_at"`
}
type instanceDomainColumns interface {
@@ -39,14 +37,21 @@ type instanceDomainColumns interface {
// IsGeneratedColumn returns the column for the is generated field.
// `qualified` indicates if the column should be qualified with the table name.
IsGeneratedColumn(qualified bool) database.Column
// TypeColumn returns the column for the type field.
// `qualified` indicates if the column should be qualified with the table name.
TypeColumn(qualified bool) database.Column
}
type instanceDomainConditions interface {
domainConditions
// TypeCondition returns a filter for the type field.
TypeCondition(typ DomainType) database.Condition
}
type instanceDomainChanges interface {
domainChanges
// SetType sets the type column.
SetType(typ DomainType) database.Change
}
type InstanceDomainRepository interface {

View File

@@ -38,17 +38,31 @@ type AddOrganizationDomain struct {
type organizationDomainColumns interface {
domainColumns
// OrgIDColumn returns the column for the org id field.
// `qualified` indicates if the column should be qualified with the table name.
OrgIDColumn(qualified bool) database.Column
// IsVerifiedColumn returns the column for the is verified field.
// `qualified` indicates if the column should be qualified with the table name.
IsVerifiedColumn(qualified bool) database.Column
// ValidationTypeColumn returns the column for the verification type field.
// `qualified` indicates if the column should be qualified with the table name.
ValidationTypeColumn(qualified bool) database.Column
}
type organizationDomainConditions interface {
domainConditions
// OrgIDCondition returns a filter on the org id field.
OrgIDCondition(orgID string) database.Condition
// IsVerifiedCondition returns a filter on the is verified field.
IsVerifiedCondition(isVerified bool) database.Condition
}
type organizationDomainChanges interface {
domainChanges
// SetVerified sets the is verified column to true.
SetVerified() database.Change
// SetValidationType sets the verification type column.
// If the domain is already verified, this is a no-op.
SetValidationType(verificationType DomainValidationType) database.Change
}
type OrganizationDomainRepository interface {

View File

@@ -3,20 +3,28 @@ CREATE TYPE zitadel.domain_validation_type AS ENUM (
, 'dns'
);
CREATE TYPE zitadel.domain_type AS ENUM (
'custom'
, 'trusted'
);
CREATE TABLE zitadel.instance_domains(
instance_id TEXT NOT NULL
, domain TEXT NOT NULL CHECK (LENGTH(domain) BETWEEN 1 AND 255)
, is_verified BOOLEAN NOT NULL DEFAULT FALSE
, is_primary BOOLEAN NOT NULL DEFAULT FALSE
, is_generated BOOLEAN NOT NULL DEFAULT FALSE
, validation_type zitadel.domain_validation_type
, is_primary BOOLEAN
, is_generated BOOLEAN
, type zitadel.domain_type NOT NULL
, created_at TIMESTAMP DEFAULT NOW()
, updated_at TIMESTAMP DEFAULT NOW()
, created_at TIMESTAMPTZ DEFAULT NOW()
, updated_at TIMESTAMPTZ DEFAULT NOW()
, PRIMARY KEY (domain)
, FOREIGN KEY (instance_id) REFERENCES zitadel.instances(id) ON DELETE CASCADE
, CONSTRAINT primary_cannot_be_trusted CHECK (is_primary IS NULL OR type != 'trusted')
, CONSTRAINT generated_cannot_be_trusted CHECK (is_generated IS NULL OR type != 'trusted')
, CONSTRAINT custom_values_set CHECK (type = 'custom' AND is_primary IS NOT NULL AND is_generated IS NOT NULL)
);
CREATE INDEX idx_instance_domain_instance ON zitadel.instance_domains(instance_id);
@@ -29,8 +37,8 @@ CREATE TABLE zitadel.org_domains(
, is_primary BOOLEAN NOT NULL DEFAULT FALSE
, validation_type zitadel.domain_validation_type
, created_at TIMESTAMP DEFAULT NOW()
, updated_at TIMESTAMP DEFAULT NOW()
, created_at TIMESTAMPTZ DEFAULT NOW()
, updated_at TIMESTAMPTZ DEFAULT NOW()
, PRIMARY KEY (instance_id, org_id, domain)
@@ -91,7 +99,8 @@ BEGIN
SET is_primary = FALSE, updated_at = NOW()
WHERE instance_id = NEW.instance_id
AND domain != NEW.domain
AND is_primary = TRUE;
AND is_primary = TRUE
AND type = 'custom';
RETURN NEW;
END;

View File

@@ -4,6 +4,7 @@ package events_test
import (
"context"
"log"
"os"
"testing"
"time"
@@ -14,11 +15,12 @@ import (
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres"
"github.com/zitadel/zitadel/internal/integration"
v2beta "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta"
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
"github.com/zitadel/zitadel/pkg/grpc/system"
)
const ConnString = "host=localhost port=5432 user=zitadel dbname=zitadel sslmode=disable"
const ConnString = "host=localhost port=5432 user=zitadel password=zitadel dbname=zitadel sslmode=disable"
var (
dbPool *pgxpool.Pool
@@ -35,12 +37,21 @@ func TestMain(m *testing.M) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
Instance = integration.NewInstance(ctx)
CTX = integration.WithSystemAuthorization(ctx)
Instance = integration.NewInstance(CTX)
CTX = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner)
SystemClient = integration.SystemClient()
OrgClient = Instance.Client.OrgV2beta
defer func() {
_, err := Instance.Client.InstanceV2Beta.DeleteInstance(CTX, &v2beta.DeleteInstanceRequest{
InstanceId: Instance.Instance.Id,
})
if err != nil {
log.Printf("Failed to delete instance on cleanup: %v\n", err)
}
}()
var err error
dbConfig, err := pgxpool.ParseConfig(ConnString)
if err != nil {

View File

@@ -0,0 +1,224 @@
//go:build integration
package events_test
import (
"log"
"testing"
"time"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
"github.com/zitadel/zitadel/internal/integration"
v2beta "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta"
"github.com/zitadel/zitadel/pkg/grpc/system"
)
func TestServer_TestInstanceDomainReduces(t *testing.T) {
instance := integration.NewInstance(CTX)
instanceRepo := repository.InstanceRepository(pool)
instanceDomainRepo := instanceRepo.Domains(true)
t.Cleanup(func() {
_, err := instance.Client.InstanceV2Beta.DeleteInstance(CTX, &v2beta.DeleteInstanceRequest{
InstanceId: instance.Instance.Id,
})
if err != nil {
t.Logf("Failed to delete instance on cleanup: %v", err)
}
})
// Wait for instance to be created
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
_, err := instanceRepo.Get(CTX,
database.WithCondition(instanceRepo.IDCondition(instance.Instance.Id)),
)
assert.NoError(ttt, err)
}, retryDuration, tick)
t.Run("test instance domain add reduces", func(t *testing.T) {
// Add a domain to the instance
domainName := gofakeit.DomainName()
beforeAdd := time.Now()
_, err := instance.Client.InstanceV2Beta.AddCustomDomain(CTX, &v2beta.AddCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
require.NoError(t, err)
afterAdd := time.Now()
t.Cleanup(func() {
_, err := instance.Client.InstanceV2Beta.RemoveCustomDomain(CTX, &v2beta.RemoveCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
if err != nil {
t.Logf("Failed to delete instance domain on cleanup: %v", err)
}
})
// Test that domain add reduces
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
domain, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.DomainCondition(database.TextOperationEqual, domainName),
),
),
)
require.NoError(ttt, err)
// event instance.domain.added
assert.Equal(ttt, domainName, domain.Domain)
assert.Equal(ttt, instance.Instance.Id, domain.InstanceID)
assert.False(ttt, domain.IsPrimary)
log.Printf("created at %v\n", domain.CreatedAt)
log.Printf("after %v\n", afterAdd)
log.Printf("before %v\n", beforeAdd)
assert.WithinRange(ttt, domain.CreatedAt, beforeAdd, afterAdd)
assert.WithinRange(ttt, domain.UpdatedAt, beforeAdd, afterAdd)
}, retryDuration, tick)
})
t.Run("test instance domain set primary reduces", func(t *testing.T) {
// Add a domain to the instance
domainName := gofakeit.DomainName()
_, err := instance.Client.InstanceV2Beta.AddCustomDomain(CTX, &v2beta.AddCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
require.NoError(t, err)
t.Cleanup(func() {
// first we change the primary domain to something else
domain, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.IsPrimaryCondition(false),
),
),
database.WithLimit(1),
)
require.NoError(t, err)
_, err = SystemClient.SetPrimaryDomain(CTX, &system.SetPrimaryDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domain.Domain,
})
require.NoError(t, err)
_, err = instance.Client.InstanceV2Beta.RemoveCustomDomain(CTX, &v2beta.RemoveCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
if err != nil {
t.Logf("Failed to delete instance domain on cleanup: %v", err)
}
})
// Wait for domain to be created
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
domain, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.DomainCondition(database.TextOperationEqual, domainName)),
),
)
require.NoError(ttt, err)
require.False(ttt, domain.IsPrimary)
assert.Equal(ttt, domainName, domain.Domain)
}, retryDuration, tick)
// Set domain as primary
beforeSetPrimary := time.Now()
_, err = SystemClient.SetPrimaryDomain(CTX, &system.SetPrimaryDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
require.NoError(t, err)
afterSetPrimary := time.Now()
// Test that set primary reduces
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
domain, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.IsPrimaryCondition(true),
),
),
)
require.NoError(ttt, err)
// event instance.domain.primary.set
assert.Equal(ttt, domainName, domain.Domain)
assert.True(ttt, domain.IsPrimary)
assert.WithinRange(ttt, domain.UpdatedAt, beforeSetPrimary, afterSetPrimary)
}, retryDuration, tick)
})
t.Run("test instance domain remove reduces", func(t *testing.T) {
// Add a domain to the instance
domainName := gofakeit.DomainName()
_, err := instance.Client.InstanceV2Beta.AddCustomDomain(CTX, &v2beta.AddCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
require.NoError(t, err)
t.Cleanup(func() {
_, err := instance.Client.InstanceV2Beta.RemoveCustomDomain(CTX, &v2beta.RemoveCustomDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
if err != nil {
t.Logf("Failed to delete instance domain on cleanup: %v", err)
}
})
// Wait for domain to be created and verify it exists
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
_, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.DomainCondition(database.TextOperationEqual, domainName),
),
),
)
require.NoError(ttt, err)
}, retryDuration, tick)
// Remove the domain
_, err = SystemClient.RemoveDomain(CTX, &system.RemoveDomainRequest{
InstanceId: instance.Instance.Id,
Domain: domainName,
})
require.NoError(t, err)
// Test that domain remove reduces
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
domain, err := instanceDomainRepo.Get(CTX,
database.WithCondition(
database.And(
instanceDomainRepo.InstanceIDCondition(instance.Instance.Id),
instanceDomainRepo.DomainCondition(database.TextOperationEqual, domainName),
),
),
)
// event instance.domain.removed
assert.Nil(ttt, domain)
require.ErrorIs(ttt, err, new(database.NoRowFoundError))
}, retryDuration, tick)
})
}

View File

@@ -17,6 +17,8 @@ import (
)
func TestServer_TestInstanceReduces(t *testing.T) {
instanceRepo := repository.InstanceRepository(pool)
t.Run("test instance add reduces", func(t *testing.T) {
instanceName := gofakeit.Name()
beforeCreate := time.Now()
@@ -33,8 +35,15 @@ func TestServer_TestInstanceReduces(t *testing.T) {
afterCreate := time.Now()
require.NoError(t, err)
t.Cleanup(func() {
_, err = SystemClient.RemoveInstance(CTX, &system.RemoveInstanceRequest{
InstanceId: instance.GetInstanceId(),
})
if err != nil {
t.Logf("Failed to delete instance on cleanup: %v", err)
}
})
instanceRepo := repository.InstanceRepository(pool)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
instance, err := instanceRepo.Get(CTX,
@@ -71,6 +80,14 @@ func TestServer_TestInstanceReduces(t *testing.T) {
},
})
require.NoError(t, err)
t.Cleanup(func() {
_, err = SystemClient.RemoveInstance(CTX, &system.RemoveInstanceRequest{
InstanceId: res.GetInstanceId(),
})
if err != nil {
t.Logf("Failed to delete instance on cleanup: %v", err)
}
})
instanceName += "new"
beforeUpdate := time.Now()
@@ -78,10 +95,9 @@ func TestServer_TestInstanceReduces(t *testing.T) {
InstanceId: res.InstanceId,
InstanceName: instanceName,
})
require.NoError(t, err)
afterUpdate := time.Now()
require.NoError(t, err)
instanceRepo := repository.InstanceRepository(pool)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
instance, err := instanceRepo.Get(CTX,
@@ -108,8 +124,6 @@ func TestServer_TestInstanceReduces(t *testing.T) {
})
require.NoError(t, err)
instanceRepo := repository.InstanceRepository(pool)
// check instance exists
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) {

View File

@@ -19,25 +19,33 @@ import (
func TestServer_TestOrganizationReduces(t *testing.T) {
instanceID := Instance.ID()
orgRepo := repository.OrganizationRepository(pool)
t.Run("test org add reduces", func(t *testing.T) {
beforeCreate := time.Now()
orgName := gofakeit.Name()
_, err := OrgClient.CreateOrganization(CTX, &v2beta_org.CreateOrganizationRequest{
org, err := OrgClient.CreateOrganization(CTX, &v2beta_org.CreateOrganizationRequest{
Name: orgName,
})
require.NoError(t, err)
afterCreate := time.Now()
orgRepo := repository.OrganizationRepository(pool)
t.Cleanup(func() {
_, err = OrgClient.DeleteOrganization(CTX, &v2beta_org.DeleteOrganizationRequest{
Id: org.GetId(),
})
if err != nil {
t.Logf("Failed to delete organization on cleanup: %v", err)
}
})
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(tt *assert.CollectT) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(org.GetId()),
orgRepo.InstanceIDCondition(instanceID),
),
),
@@ -63,6 +71,15 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
})
require.NoError(t, err)
t.Cleanup(func() {
_, err = OrgClient.DeleteOrganization(CTX, &v2beta_org.DeleteOrganizationRequest{
Id: organization.Id,
})
if err != nil {
t.Logf("Failed to delete organization on cleanup: %v", err)
}
})
// 2. update org name
beforeUpdate := time.Now()
orgName = orgName + "_new"
@@ -73,14 +90,12 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
require.NoError(t, err)
afterUpdate := time.Now()
orgRepo := repository.OrganizationRepository(pool)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(t *assert.CollectT) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),
@@ -101,6 +116,15 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
Name: orgName,
})
require.NoError(t, err)
t.Cleanup(func() {
// Cleanup: delete the organization
_, err = OrgClient.DeleteOrganization(CTX, &v2beta_org.DeleteOrganizationRequest{
Id: organization.Id,
})
if err != nil {
t.Logf("Failed to delete organization on cleanup: %v", err)
}
})
// 2. deactivate org name
beforeDeactivate := time.Now()
@@ -111,14 +135,12 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
require.NoError(t, err)
afterDeactivate := time.Now()
orgRepo := repository.OrganizationRepository(pool)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(t *assert.CollectT) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),
@@ -126,7 +148,6 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
require.NoError(t, err)
// event org.deactivate
assert.Equal(t, orgName, organization.Name)
assert.Equal(t, domain.OrgStateInactive, organization.State)
assert.WithinRange(t, organization.UpdatedAt, beforeDeactivate, afterDeactivate)
}, retryDuration, tick)
@@ -140,6 +161,15 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
Name: orgName,
})
require.NoError(t, err)
t.Cleanup(func() {
// Cleanup: delete the organization
_, err = OrgClient.DeleteOrganization(CTX, &v2beta_org.DeleteOrganizationRequest{
Id: organization.Id,
})
if err != nil {
t.Logf("Failed to delete organization on cleanup: %v", err)
}
})
// 2. deactivate org name
_, err = OrgClient.DeactivateOrganization(CTX, &v2beta_org.DeactivateOrganizationRequest{
@@ -154,14 +184,12 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),
)
require.NoError(t, err)
assert.Equal(t, orgName, organization.Name)
assert.Equal(t, domain.OrgStateInactive, organization.State)
}, retryDuration, tick)
@@ -178,7 +206,7 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),
@@ -201,24 +229,19 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
})
require.NoError(t, err)
// 2. check org retrivable
// 2. check org retrievable
orgRepo := repository.OrganizationRepository(pool)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
assert.EventuallyWithT(t, func(t *assert.CollectT) {
organization, err := orgRepo.Get(CTX,
_, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),
)
require.NoError(t, err)
if organization == nil {
assert.Fail(t, "this error is here because of a race condition")
}
assert.Equal(t, orgName, organization.Name)
}, retryDuration, tick)
// 3. delete org
@@ -232,7 +255,7 @@ func TestServer_TestOrganizationReduces(t *testing.T) {
organization, err := orgRepo.Get(CTX,
database.WithCondition(
database.And(
orgRepo.NameCondition(orgName),
orgRepo.IDCondition(organization.Id),
orgRepo.InstanceIDCondition(instanceID),
),
),

View File

@@ -30,7 +30,7 @@ func InstanceRepository(client database.QueryExecutor) domain.InstanceRepository
const (
queryInstanceStmt = `SELECT instances.id, instances.name, instances.default_org_id, instances.iam_project_id, instances.console_client_id, instances.console_app_id, instances.default_language, instances.created_at, instances.updated_at` +
` , CASE WHEN count(instance_domains.domain) > 0 THEN jsonb_agg(json_build_object('domain', instance_domains.domain, 'isVerified', instance_domains.is_verified, 'isPrimary', instance_domains.is_primary, 'isGenerated', instance_domains.is_generated, 'validationType', instance_domains.validation_type, 'createdAt', instance_domains.created_at, 'updatedAt', instance_domains.updated_at)) ELSE NULL::JSONB END domains` +
` , CASE WHEN count(instance_domains.domain) > 0 THEN jsonb_agg(json_build_object('domain', instance_domains.domain, 'isPrimary', instance_domains.is_primary, 'isGenerated', instance_domains.is_generated, 'createdAt', instance_domains.created_at, 'updatedAt', instance_domains.updated_at)) ELSE NULL::JSONB END domains` +
` FROM zitadel.instances`
)

View File

@@ -19,7 +19,7 @@ type instanceDomain struct {
// repository
// -------------------------------------------------------------
const queryInstanceDomainStmt = `SELECT instance_domains.instance_id, instance_domains.domain, instance_domains.is_verified, instance_domains.is_primary, instance_domains.validation_type, instance_domains.created_at, instance_domains.updated_at ` +
const queryInstanceDomainStmt = `SELECT instance_domains.instance_id, instance_domains.domain, instance_domains.is_primary, instance_domains.created_at, instance_domains.updated_at ` +
`FROM zitadel.instance_domains`
// Get implements [domain.InstanceDomainRepository].
@@ -56,11 +56,11 @@ func (i *instanceDomain) List(ctx context.Context, opts ...database.QueryOption)
func (i *instanceDomain) Add(ctx context.Context, domain *domain.AddInstanceDomain) error {
var builder database.StatementBuilder
builder.WriteString(`INSERT INTO zitadel.instance_domains (instance_id, domain, is_verified, is_primary, validation_type) VALUES (`)
builder.WriteArgs(domain.InstanceID, domain.Domain, domain.IsVerified, domain.IsPrimary, domain.ValidationType)
builder.WriteString(`INSERT INTO zitadel.instance_domains (instance_id, domain, is_primary, is_generated, type, created_at, updated_at) VALUES (`)
builder.WriteArgs(domain.InstanceID, domain.Domain, domain.IsPrimary, domain.IsGenerated, domain.Type, domain.CreatedAt, domain.UpdatedAt)
builder.WriteString(`) RETURNING created_at, updated_at`)
return i.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&domain.CreatedAt, &domain.UpdatedAt)
return i.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(domain.CreatedAt, domain.UpdatedAt)
}
// Remove implements [domain.InstanceDomainRepository].
@@ -93,26 +93,21 @@ func (i *instanceDomain) Update(ctx context.Context, condition database.Conditio
// changes
// -------------------------------------------------------------
// SetValidationType implements [domain.InstanceDomainRepository].
func (i instanceDomain) SetValidationType(verificationType domain.DomainValidationType) database.Change {
return database.NewChange(i.ValidationTypeColumn(false), verificationType)
}
// SetPrimary implements [domain.InstanceDomainRepository].
func (i instanceDomain) SetPrimary() database.Change {
return database.NewChange(i.IsPrimaryColumn(false), true)
}
// SetVerified implements [domain.InstanceDomainRepository].
func (i instanceDomain) SetVerified() database.Change {
return database.NewChange(i.IsVerifiedColumn(false), true)
}
// SetUpdatedAt implements [domain.OrganizationDomainRepository].
func (i instanceDomain) SetUpdatedAt(updatedAt time.Time) database.Change {
return database.NewChange(i.UpdatedAtColumn(false), updatedAt)
}
// SetType implements [domain.InstanceDomainRepository].
func (i instanceDomain) SetType(typ domain.DomainType) database.Change {
return database.NewChange(i.TypeColumn(false), typ)
}
// -------------------------------------------------------------
// conditions
// -------------------------------------------------------------
@@ -132,9 +127,9 @@ func (i instanceDomain) IsPrimaryCondition(isPrimary bool) database.Condition {
return database.NewBooleanCondition(i.IsPrimaryColumn(true), isPrimary)
}
// IsVerifiedCondition implements [domain.InstanceDomainRepository].
func (i instanceDomain) IsVerifiedCondition(isVerified bool) database.Condition {
return database.NewBooleanCondition(i.IsVerifiedColumn(true), isVerified)
// TypeCondition implements [domain.InstanceDomainRepository].
func (i instanceDomain) TypeCondition(typ domain.DomainType) database.Condition {
return database.NewTextCondition(i.TypeColumn(true), database.TextOperationEqual, typ)
}
// -------------------------------------------------------------
@@ -174,14 +169,6 @@ func (instanceDomain) IsPrimaryColumn(qualified bool) database.Column {
return database.NewColumn("is_primary")
}
// IsVerifiedColumn implements [domain.InstanceDomainRepository].
func (instanceDomain) IsVerifiedColumn(qualified bool) database.Column {
if qualified {
return database.NewColumn("instance_domains.is_verified")
}
return database.NewColumn("is_verified")
}
// UpdatedAtColumn implements [domain.InstanceDomainRepository].
// Subtle: this method shadows the method ([domain.InstanceRepository]).UpdatedAtColumn of instanceDomain.instance.
func (instanceDomain) UpdatedAtColumn(qualified bool) database.Column {
@@ -191,14 +178,6 @@ func (instanceDomain) UpdatedAtColumn(qualified bool) database.Column {
return database.NewColumn("updated_at")
}
// ValidationTypeColumn implements [domain.InstanceDomainRepository].
func (instanceDomain) ValidationTypeColumn(qualified bool) database.Column {
if qualified {
return database.NewColumn("instance_domains.validation_type")
}
return database.NewColumn("validation_type")
}
// IsGeneratedColumn implements [domain.InstanceDomainRepository].
func (instanceDomain) IsGeneratedColumn(qualified bool) database.Column {
if qualified {
@@ -207,6 +186,14 @@ func (instanceDomain) IsGeneratedColumn(qualified bool) database.Column {
return database.NewColumn("is_generated")
}
// TypeColumn implements [domain.InstanceDomainRepository].
func (instanceDomain) TypeColumn(qualified bool) database.Column {
if qualified {
return database.NewColumn("instance_domains.type")
}
return database.NewColumn("type")
}
// -------------------------------------------------------------
// scanners
// -------------------------------------------------------------

View File

@@ -5,7 +5,6 @@ import (
"testing"
"github.com/brianvoe/gofakeit/v6"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -39,41 +38,25 @@ func TestAddInstanceDomain(t *testing.T) {
{
name: "happy path",
instanceDomain: domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
},
},
{
name: "add verified domain",
instanceDomain: domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: true,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsPrimary: false,
},
},
{
name: "add primary domain",
instanceDomain: domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsPrimary: true,
},
},
{
name: "add domain without domain name",
instanceDomain: domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: "",
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: "",
IsPrimary: false,
},
err: new(database.CheckError),
},
@@ -83,11 +66,9 @@ func TestAddInstanceDomain(t *testing.T) {
domainName := gofakeit.DomainName()
instanceDomain := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName,
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName,
IsPrimary: false,
}
err := domainRepo.Add(ctx, instanceDomain)
@@ -95,11 +76,9 @@ func TestAddInstanceDomain(t *testing.T) {
// return same domain again
return &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName,
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
InstanceID: instanceID,
Domain: domainName,
IsPrimary: true,
}
},
err: new(database.UniqueError),
@@ -107,21 +86,17 @@ func TestAddInstanceDomain(t *testing.T) {
{
name: "add domain with non-existent instance",
instanceDomain: domain.AddInstanceDomain{
InstanceID: "non-existent-instance",
Domain: gofakeit.DomainName(),
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: "non-existent-instance",
Domain: gofakeit.DomainName(),
IsPrimary: false,
},
err: new(database.ForeignKeyError),
},
{
name: "add domain without instance id",
instanceDomain: domain.AddInstanceDomain{
Domain: gofakeit.DomainName(),
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
Domain: gofakeit.DomainName(),
IsPrimary: false,
},
err: new(database.ForeignKeyError),
},
@@ -186,18 +161,14 @@ func TestGetInstanceDomain(t *testing.T) {
domainName2 := gofakeit.DomainName()
domain1 := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName1,
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName1,
IsPrimary: true,
}
domain2 := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName2,
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
InstanceID: instanceID,
Domain: domainName2,
IsPrimary: false,
}
err = domainRepo.Add(t.Context(), domain1)
@@ -217,11 +188,9 @@ func TestGetInstanceDomain(t *testing.T) {
database.WithCondition(domainRepo.IsPrimaryCondition(true)),
},
expected: &domain.InstanceDomain{
InstanceID: instanceID,
Domain: domainName1,
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName1,
IsPrimary: true,
},
},
{
@@ -230,24 +199,9 @@ func TestGetInstanceDomain(t *testing.T) {
database.WithCondition(domainRepo.DomainCondition(database.TextOperationEqual, domainName2)),
},
expected: &domain.InstanceDomain{
InstanceID: instanceID,
Domain: domainName2,
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
},
},
{
name: "get verified domain",
opts: []database.QueryOption{
database.WithCondition(domainRepo.IsVerifiedCondition(true)),
},
expected: &domain.InstanceDomain{
InstanceID: instanceID,
Domain: domainName1,
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName2,
IsPrimary: false,
},
},
{
@@ -272,9 +226,7 @@ func TestGetInstanceDomain(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, test.expected.InstanceID, result.InstanceID)
assert.Equal(t, test.expected.Domain, result.Domain)
assert.Equal(t, test.expected.IsVerified, result.IsVerified)
assert.Equal(t, test.expected.IsPrimary, result.IsPrimary)
assert.Equal(t, test.expected.ValidationType, result.ValidationType)
assert.NotEmpty(t, result.CreatedAt)
assert.NotEmpty(t, result.UpdatedAt)
})
@@ -307,25 +259,19 @@ func TestListInstanceDomains(t *testing.T) {
domainRepo := instanceRepo.Domains(false)
domains := []domain.AddInstanceDomain{
{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsPrimary: true,
},
{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsPrimary: false,
},
{
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsVerified: true,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: gofakeit.DomainName(),
IsPrimary: false,
},
}
@@ -344,13 +290,6 @@ func TestListInstanceDomains(t *testing.T) {
opts: []database.QueryOption{},
expectedCount: 3,
},
{
name: "list verified domains",
opts: []database.QueryOption{
database.WithCondition(domainRepo.IsVerifiedCondition(true)),
},
expectedCount: 2,
},
{
name: "list primary domains",
opts: []database.QueryOption{
@@ -419,11 +358,9 @@ func TestUpdateInstanceDomain(t *testing.T) {
domainRepo := instanceRepo.Domains(false)
domainName := gofakeit.DomainName()
instanceDomain := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName,
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName,
IsPrimary: false,
}
err = domainRepo.Add(t.Context(), instanceDomain)
@@ -436,38 +373,16 @@ func TestUpdateInstanceDomain(t *testing.T) {
expected int64
err error
}{
{
name: "set verified",
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
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 validation type",
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
changes: []database.Change{domainRepo.SetValidationType(domain.DomainValidationTypeHTTP)},
expected: 1,
},
{
name: "multiple changes",
condition: domainRepo.DomainCondition(database.TextOperationEqual, domainName),
changes: []database.Change{
domainRepo.SetVerified(),
domainRepo.SetPrimary(),
domainRepo.SetValidationType(domain.DomainValidationTypeDNS),
},
expected: 1,
},
{
name: "update non-existent domain",
condition: domainRepo.DomainCondition(database.TextOperationEqual, "non-existent.com"),
changes: []database.Change{domainRepo.SetVerified()},
changes: []database.Change{domainRepo.SetPrimary()},
expected: 0,
},
{
@@ -533,18 +448,14 @@ func TestRemoveInstanceDomain(t *testing.T) {
domainName2 := gofakeit.DomainName()
domain1 := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName1,
IsVerified: true,
IsPrimary: true,
ValidationType: gu.Ptr(domain.DomainValidationTypeDNS),
InstanceID: instanceID,
Domain: domainName1,
IsPrimary: true,
}
domain2 := &domain.AddInstanceDomain{
InstanceID: instanceID,
Domain: domainName2,
IsVerified: false,
IsPrimary: false,
ValidationType: gu.Ptr(domain.DomainValidationTypeHTTP),
InstanceID: instanceID,
Domain: domainName2,
IsPrimary: false,
}
err = domainRepo.Add(t.Context(), domain1)
@@ -628,16 +539,6 @@ func TestInstanceDomainConditions(t *testing.T) {
condition: domainRepo.IsPrimaryCondition(false),
expected: "instance_domains.is_primary = $1",
},
{
name: "is verified true",
condition: domainRepo.IsVerifiedCondition(true),
expected: "instance_domains.is_verified = $1",
},
{
name: "is verified false",
condition: domainRepo.IsVerifiedCondition(false),
expected: "instance_domains.is_verified = $1",
},
}
for _, test := range tests {
@@ -658,26 +559,11 @@ func TestInstanceDomainChanges(t *testing.T) {
change database.Change
expected string
}{
{
name: "set verified",
change: domainRepo.SetVerified(),
expected: "is_verified = $1",
},
{
name: "set primary",
change: domainRepo.SetPrimary(),
expected: "is_primary = $1",
},
{
name: "set validation type DNS",
change: domainRepo.SetValidationType(domain.DomainValidationTypeDNS),
expected: "validation_type = $1",
},
{
name: "set validation type HTTP",
change: domainRepo.SetValidationType(domain.DomainValidationTypeHTTP),
expected: "validation_type = $1",
},
}
for _, test := range tests {

View File

@@ -9,6 +9,7 @@ Database:
SSL:
Mode: disable
Admin:
Username: zitadel
Username: adlerhurst
Password: password
SSL:
Mode: disable

View File

@@ -4,6 +4,8 @@ import (
"context"
"database/sql"
"github.com/muhlemmer/gu"
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database"
v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql"
@@ -31,7 +33,7 @@ func (p *instanceDomainRelationalProjection) Reducers() []handler.AggregateReduc
EventReducers: []handler.EventReducer{
{
Event: instance.InstanceDomainAddedEventType,
Reduce: p.reduceDomainAdded,
Reduce: p.reduceCustomDomainAdded,
},
{
Event: instance.InstanceDomainPrimarySetEventType,
@@ -39,14 +41,22 @@ func (p *instanceDomainRelationalProjection) Reducers() []handler.AggregateReduc
},
{
Event: instance.InstanceDomainRemovedEventType,
Reduce: p.reduceDomainRemoved,
Reduce: p.reduceCustomDomainRemoved,
},
{
Event: instance.TrustedDomainAddedEventType,
Reduce: p.reduceTrustedDomainAdded,
},
{
Event: instance.TrustedDomainRemovedEventType,
Reduce: p.reduceTrustedDomainRemoved,
},
},
},
}
}
func (p *instanceDomainRelationalProjection) reduceDomainAdded(event eventstore.Event) (*handler.Statement, error) {
func (p *instanceDomainRelationalProjection) reduceCustomDomainAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.DomainAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-DU0xF", "reduce.wrong.event.type %s", instance.InstanceDomainAddedEventType)
@@ -60,8 +70,9 @@ func (p *instanceDomainRelationalProjection) reduceDomainAdded(event eventstore.
InstanceID: e.Aggregate().InstanceID,
Domain: e.Domain,
IsGenerated: e.Generated,
CreatedAt: e.CreationDate(),
UpdatedAt: e.CreationDate(),
Type: domain.DomainTypeCustom,
CreatedAt: gu.Ptr(e.CreationDate()),
UpdatedAt: gu.Ptr(e.CreationDate()),
})
}), nil
}
@@ -81,6 +92,7 @@ func (p *instanceDomainRelationalProjection) reduceDomainPrimarySet(event events
database.And(
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
domainRepo.DomainCondition(database.TextOperationEqual, e.Domain),
domainRepo.TypeCondition(domain.DomainTypeCustom),
),
domainRepo.SetPrimary(),
domainRepo.SetUpdatedAt(e.CreationDate()),
@@ -89,7 +101,7 @@ func (p *instanceDomainRelationalProjection) reduceDomainPrimarySet(event events
}), nil
}
func (p *instanceDomainRelationalProjection) reduceDomainRemoved(event eventstore.Event) (*handler.Statement, error) {
func (p *instanceDomainRelationalProjection) reduceCustomDomainRemoved(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.DomainRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Hhcdl", "reduce.wrong.event.type %s", instance.InstanceDomainRemovedEventType)
@@ -104,6 +116,49 @@ func (p *instanceDomainRelationalProjection) reduceDomainRemoved(event eventstor
database.And(
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
domainRepo.DomainCondition(database.TextOperationEqual, e.Domain),
domainRepo.TypeCondition(domain.DomainTypeCustom),
),
)
return err
}), nil
}
func (p *instanceDomainRelationalProjection) reduceTrustedDomainAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.TrustedDomainAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-svHDh", "reduce.wrong.event.type %s", instance.TrustedDomainAddedEventType)
}
return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error {
tx, ok := ex.(*sql.Tx)
if !ok {
return zerrors.ThrowInvalidArgumentf(nil, "HANDL-gx7tQ", "reduce.wrong.db.pool %T", ex)
}
return repository.InstanceRepository(v3_sql.SQLTx(tx)).Domains(false).Add(ctx, &domain.AddInstanceDomain{
InstanceID: e.Aggregate().InstanceID,
Domain: e.Domain,
Type: domain.DomainTypeCustom,
CreatedAt: gu.Ptr(e.CreationDate()),
UpdatedAt: gu.Ptr(e.CreationDate()),
})
}), nil
}
func (p *instanceDomainRelationalProjection) reduceTrustedDomainRemoved(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.TrustedDomainRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-4K74E", "reduce.wrong.event.type %s", instance.TrustedDomainRemovedEventType)
}
return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error {
tx, ok := ex.(*sql.Tx)
if !ok {
return zerrors.ThrowInvalidArgumentf(nil, "HANDL-D68ap", "reduce.wrong.db.pool %T", ex)
}
domainRepo := repository.InstanceRepository(v3_sql.SQLTx(tx)).Domains(false)
_, err := domainRepo.Remove(ctx,
database.And(
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
domainRepo.DomainCondition(database.TextOperationEqual, e.Domain),
domainRepo.TypeCondition(domain.DomainTypeTrusted),
),
)
return err

View File

@@ -8,6 +8,7 @@ import (
"github.com/zitadel/zitadel/backend/v3/storage/database"
v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql"
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
old_domain "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/repository/org"
@@ -31,22 +32,30 @@ func (p *orgDomainRelationalProjection) Reducers() []handler.AggregateReducer {
EventReducers: []handler.EventReducer{
{
Event: org.OrgDomainAddedEventType,
Reduce: p.reduceDomainAdded,
Reduce: p.reduceAdded,
},
{
Event: org.OrgDomainPrimarySetEventType,
Reduce: p.reduceDomainPrimarySet,
Reduce: p.reducePrimarySet,
},
{
Event: org.OrgDomainRemovedEventType,
Reduce: p.reduceDomainRemoved,
Reduce: p.reduceRemoved,
},
{
Event: org.OrgDomainVerificationAddedEventType,
Reduce: p.reduceVerificationAdded,
},
{
Event: org.OrgDomainVerifiedEventType,
Reduce: p.reduceVerified,
},
},
},
}
}
func (p *orgDomainRelationalProjection) reduceDomainAdded(event eventstore.Event) (*handler.Statement, error) {
func (p *orgDomainRelationalProjection) reduceAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.DomainAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-ZX9Fw", "reduce.wrong.event.type %s", org.OrgDomainAddedEventType)
@@ -66,7 +75,7 @@ func (p *orgDomainRelationalProjection) reduceDomainAdded(event eventstore.Event
}), nil
}
func (p *orgDomainRelationalProjection) reduceDomainPrimarySet(event eventstore.Event) (*handler.Statement, error) {
func (p *orgDomainRelationalProjection) reducePrimarySet(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.DomainPrimarySetEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-dmFdb", "reduce.wrong.event.type %s", org.OrgDomainPrimarySetEventType)
@@ -90,7 +99,7 @@ func (p *orgDomainRelationalProjection) reduceDomainPrimarySet(event eventstore.
}), nil
}
func (p *orgDomainRelationalProjection) reduceDomainRemoved(event eventstore.Event) (*handler.Statement, error) {
func (p *orgDomainRelationalProjection) reduceRemoved(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.DomainRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-MzC0n", "reduce.wrong.event.type %s", org.OrgDomainRemovedEventType)
@@ -111,3 +120,58 @@ func (p *orgDomainRelationalProjection) reduceDomainRemoved(event eventstore.Eve
return err
}), nil
}
func (p *orgDomainRelationalProjection) reduceVerificationAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.DomainVerificationAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-oGzip", "reduce.wrong.event.type %s", org.OrgDomainVerificationAddedEventType)
}
var validationType domain.DomainValidationType
switch e.ValidationType {
case old_domain.OrgDomainValidationTypeDNS:
validationType = domain.DomainValidationTypeDNS
case old_domain.OrgDomainValidationTypeHTTP:
validationType = domain.DomainValidationTypeHTTP
case old_domain.OrgDomainValidationTypeUnspecified:
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-FJfKB", "reduce.unsupported.validation.type %v", e.ValidationType)
}
return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error {
tx, ok := ex.(*sql.Tx)
if !ok {
return zerrors.ThrowInvalidArgumentf(nil, "HANDL-yF03i", "reduce.wrong.db.pool %T", ex)
}
domainRepo := repository.OrganizationRepository(v3_sql.SQLTx(tx)).Domains(false)
_, err := domainRepo.Update(ctx,
database.And(
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner),
domainRepo.DomainCondition(database.TextOperationEqual, e.Domain),
),
domainRepo.SetValidationType(validationType),
)
return err
}), nil
}
func (p *orgDomainRelationalProjection) reduceVerified(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.DomainVerifiedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-7WrI2", "reduce.wrong.event.type %s", org.OrgDomainVerifiedEventType)
}
return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error {
tx, ok := ex.(*sql.Tx)
if !ok {
return zerrors.ThrowInvalidArgumentf(nil, "HANDL-0ZGqC", "reduce.wrong.db.pool %T", ex)
}
domainRepo := repository.OrganizationRepository(v3_sql.SQLTx(tx)).Domains(false)
_, err := domainRepo.Update(ctx,
database.And(
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner),
domainRepo.DomainCondition(database.TextOperationEqual, e.Domain),
),
domainRepo.SetVerified(),
)
return err
}), nil
}