mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-23 14:16:42 +00:00
This pull request introduces a new feature that allows adding, updating, and querying metadata for organizations. The changes are primarily in the backend and include new database tables, repositories, and domain logic to support organization metadata. ## Changes * New `org_metadata` table: A new table `zitadel.org_metadata` is introduced to store metadata for organizations. It includes columns for `instance_id`, `org_id`, `key`, and `value`. The `value` is stored as a `BYTEA` type to allow for flexible data storage. * New `OrganizationMetadataRepository`: A new repository `OrganizationMetadataRepository` is created to handle all database operations for organization metadata. It provides methods to `Get`, `List`, `Set`, and `Remove` metadata. * New `org_metadata_relational_projection`: A new projection `org_metadata_relational_projection` is added to update the `zitadel.org_metadata` table based on events. It handles `MetadataSet`, `MetadataRemoved`, and `MetadataRemovedAll` events. * Updated `OrganizationRepository`: The `OrganizationRepository` is updated to support loading organization metadata. A new method `LoadMetadata` is added to enable joining the `org_metadata` table when querying for organizations. * Updated Organization domain: The Organization domain model is updated to include a new field `Metadata` of type `[]*OrganizationMetadata`. ## Additional Info * Extensible Design: The new metadata feature is designed to be extensible, allowing for future enhancements such as indexing on specific JSON fields within the `value` column. * closes https://github.com/zitadel/zitadel/issues/10206 * closes https://github.com/zitadel/zitadel/issues/10214
115 lines
3.9 KiB
Go
115 lines
3.9 KiB
Go
package projection
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"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"
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
|
"github.com/zitadel/zitadel/internal/repository/org"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type orgMetadataRelationalProjection struct{}
|
|
|
|
func newOrgMetadataRelationalProjection(ctx context.Context, config handler.Config) *handler.Handler {
|
|
return handler.NewHandler(ctx, &config, new(orgMetadataRelationalProjection))
|
|
}
|
|
|
|
func (*orgMetadataRelationalProjection) Name() string {
|
|
return "zitadel.org_metadata"
|
|
}
|
|
|
|
func (p *orgMetadataRelationalProjection) Reducers() []handler.AggregateReducer {
|
|
return []handler.AggregateReducer{
|
|
{
|
|
Aggregate: org.AggregateType,
|
|
EventReducers: []handler.EventReducer{
|
|
{
|
|
Event: org.MetadataSetType,
|
|
Reduce: p.reduceSet,
|
|
},
|
|
{
|
|
Event: org.MetadataRemovedType,
|
|
Reduce: p.reduceRemoved,
|
|
},
|
|
// This event cannot be tested because it was never used in the past
|
|
{
|
|
Event: org.MetadataRemovedAllType,
|
|
Reduce: p.reduceRemovedAll,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *orgMetadataRelationalProjection) reduceSet(event eventstore.Event) (*handler.Statement, error) {
|
|
e, ok := event.(*org.MetadataSetEvent)
|
|
if !ok {
|
|
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-xOO4e", "reduce.wrong.event.type %s", org.MetadataSetType)
|
|
}
|
|
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-xg4IJ", "reduce.wrong.db.pool %T", ex)
|
|
}
|
|
return repository.OrganizationMetadataRepository().Set(ctx, v3_sql.SQLTx(tx), &domain.OrganizationMetadata{
|
|
Metadata: domain.Metadata{
|
|
InstanceID: e.Aggregate().InstanceID,
|
|
Key: e.Key,
|
|
Value: e.Value,
|
|
CreatedAt: e.CreationDate(),
|
|
UpdatedAt: e.CreationDate(),
|
|
},
|
|
OrganizationID: e.Aggregate().ResourceOwner,
|
|
})
|
|
}), nil
|
|
}
|
|
|
|
func (p *orgMetadataRelationalProjection) reduceRemoved(event eventstore.Event) (*handler.Statement, error) {
|
|
e, ok := event.(*org.MetadataRemovedEvent)
|
|
if !ok {
|
|
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-XE6TF", "reduce.wrong.event.type %s", org.MetadataRemovedType)
|
|
}
|
|
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-QKMlz", "reduce.wrong.db.pool %T", ex)
|
|
}
|
|
domainRepo := repository.OrganizationMetadataRepository()
|
|
_, err := domainRepo.Remove(ctx, v3_sql.SQLTx(tx),
|
|
database.And(
|
|
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
|
|
domainRepo.OrganizationIDCondition(e.Aggregate().ResourceOwner),
|
|
domainRepo.KeyCondition(database.TextOperationEqual, e.Key),
|
|
),
|
|
)
|
|
return err
|
|
}), nil
|
|
}
|
|
|
|
func (p *orgMetadataRelationalProjection) reduceRemovedAll(event eventstore.Event) (*handler.Statement, error) {
|
|
e, ok := event.(*org.MetadataRemovedAllEvent)
|
|
if !ok {
|
|
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-EmyAe", "reduce.wrong.event.type %s", org.MetadataRemovedAllType)
|
|
}
|
|
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-hjEHg", "reduce.wrong.db.pool %T", ex)
|
|
}
|
|
domainRepo := repository.OrganizationMetadataRepository()
|
|
_, err := domainRepo.Remove(ctx, v3_sql.SQLTx(tx),
|
|
database.And(
|
|
domainRepo.InstanceIDCondition(e.Aggregate().InstanceID),
|
|
domainRepo.OrganizationIDCondition(e.Aggregate().ResourceOwner),
|
|
),
|
|
)
|
|
return err
|
|
}), nil
|
|
}
|