feat: delete metadata on SetOrganizationMetadata api (#11790)

# Which Problems Are Solved

Currently, to delete an Organization metadata key, callers must use a
separate
[DeleteMetadata](https://zitadel.com/docs/reference/api/org/zitadel.org.v2.OrganizationService.DeleteOrganizationMetadata)
API. This increases the complexity on client-side for operations where
metadata need to be synchronized.

# How the Problems Are Solved

Introduce deletion behavior to
[SetOrganizationMetadata](https://zitadel.com/docs/reference/api/org/zitadel.org.v2.OrganizationService.SetOrganizationMetadata):

When a metadata entry is passed with an empty value ("" / empty bytes):

- If the key exists → delete the key
- If the key does not exist → no-op (idempotent, no error)

# Additional Changes

n/a

# Additional Context

Relates to #11776

---------

Co-authored-by: Gayathri Vijayan <[email protected]>
Co-authored-by: Gayathri Vijayan <[email protected]>
This commit is contained in:
Vitor Bari Buccianti
2026-03-10 10:09:51 +00:00
committed by GitHub
co-authored by Gayathri Vijayan Gayathri Vijayan
parent 8b90ddb0b5
commit 62cb5bbb83
9 changed files with 267 additions and 142 deletions
+15
View File
@@ -12,6 +12,7 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
resources_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
@@ -176,3 +177,17 @@ func diffProto(expected, actual proto.Message) string {
}
return "\n\nDiff:\n" + diff
}
// AssertMetadataEquals verifies that two slices of proto Metadata are equal, comparing both length and content by key-value mapping.
func AssertMetadataEquals(t assert.TestingT, expected []*metadata.Metadata, actual []*metadata.Metadata) {
assert.Equal(t, len(expected), len(actual), "metadata length mismatch")
assert.Equal(t, getMetadataMap(expected), getMetadataMap(actual), "metadata content mismatch")
}
func getMetadataMap(metadataEntries []*metadata.Metadata) map[string][]byte {
metadataByKey := make(map[string][]byte, len(metadataEntries))
for _, md := range metadataEntries {
metadataByKey[md.Key] = md.Value
}
return metadataByKey
}
+13
View File
@@ -392,6 +392,19 @@ func (i *Instance) CreateOrganization(ctx context.Context, name, adminEmail stri
return resp
}
func (i *Instance) SetOrganizationMetadata(ctx context.Context, id, key, value string) *org.SetOrganizationMetadataResponse {
resp, err := i.Client.OrgV2.SetOrganizationMetadata(ctx, &org.SetOrganizationMetadataRequest{
OrganizationId: id,
Metadata: []*org.Metadata{{
Key: key,
Value: []byte(base64.StdEncoding.EncodeToString([]byte(value))),
},
},
})
logging.OnError(err).Panic("set organization metadata")
return resp
}
func (i *Instance) DeactivateOrganization(ctx context.Context, orgID string) *mgmt.DeactivateOrgResponse {
resp, err := i.Client.Mgmt.DeactivateOrg(
SetOrgID(ctx, orgID),