feat(relational): org metadata (#10761)

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
This commit is contained in:
Silvan
2025-10-14 10:15:16 +01:00
committed by GitHub
parent 6b6f94b9b3
commit 6686c3b16b
18 changed files with 1968 additions and 49 deletions
@@ -21,6 +21,11 @@ func wrapError(err error) error {
return database.NewNoRowFoundError(err)
}
var pgxErr *pgconn.PgError
if errors.As(err, &pgxErr) {
return wrapPgError(pgxErr)
}
// scany only exports its errors as strings
if strings.HasPrefix(err.Error(), "scany: expected 1 row, got: ") {
return database.NewMultipleRowsFoundError(err)
@@ -29,23 +34,24 @@ func wrapError(err error) error {
return database.NewScanError(err)
}
var pgxErr *pgconn.PgError
if !errors.As(err, &pgxErr) {
return database.NewUnknownError(err)
}
switch pgxErr.Code {
// 23514: check_violation - A value violates a CHECK constraint.
case "23514":
return database.NewCheckError(pgxErr.TableName, pgxErr.ConstraintName, pgxErr)
// 23505: unique_violation - A value violates a UNIQUE constraint.
case "23505":
return database.NewUniqueError(pgxErr.TableName, pgxErr.ConstraintName, pgxErr)
// 23503: foreign_key_violation - A value violates a foreign key constraint.
case "23503":
return database.NewForeignKeyError(pgxErr.TableName, pgxErr.ConstraintName, pgxErr)
// 23502: not_null_violation - A value violates a NOT NULL constraint.
case "23502":
return database.NewNotNullError(pgxErr.TableName, pgxErr.ConstraintName, pgxErr)
}
return database.NewUnknownError(err)
}
func wrapPgError(err *pgconn.PgError) error {
switch err.Code {
// 23514: check_violation - A value violates a CHECK constraint.
case "23514":
return database.NewCheckError(err.TableName, err.ConstraintName, err)
// 23505: unique_violation - A value violates a UNIQUE constraint.
case "23505":
return database.NewUniqueError(err.TableName, err.ConstraintName, err)
// 23503: foreign_key_violation - A value violates a foreign key constraint.
case "23503":
return database.NewForeignKeyError(err.TableName, err.ConstraintName, err)
// 23502: not_null_violation - A value violates a NOT NULL constraint.
case "23502":
return database.NewNotNullError(err.TableName, err.ConstraintName, err)
default:
return database.NewUnknownError(err)
}
}