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
@@ -4,6 +4,7 @@ package org_test
import (
"context"
"encoding/base64"
"errors"
"os"
"slices"
@@ -17,6 +18,7 @@ import (
"github.com/zitadel/zitadel/internal/integration"
"github.com/zitadel/zitadel/pkg/grpc/admin"
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
)
@@ -280,7 +282,7 @@ func TestServer_UpdateOrganization(t *testing.T) {
name: "update org with non existent org id",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
req: &org.UpdateOrganizationRequest{
OrganizationId: "non existant org id",
OrganizationId: "non existent org id",
// Name: "",
},
wantErr: true,
@@ -1029,125 +1031,159 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
}
func TestServer_SetOrganizationMetadata(t *testing.T) {
orgs, _, _ := createOrgs(CTX, t, Client, 1)
orgId := orgs[0].OrganizationId
tests := []struct {
name string
ctx context.Context
setupFunc func()
orgId string
key string
value string
setupFunc func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest)
req *org.SetOrganizationMetadataRequest
assert func(ttt *assert.CollectT, setMetadataResponse *org.SetOrganizationMetadataResponse, testStartTime time.Time, listMetadataResponse *org.ListOrganizationMetadataResponse)
err error
}{
{
name: "no permission",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
orgId: orgId,
key: "key1",
value: "value1",
setupFunc: func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = orgID
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
},
},
err: errors.New("membership not found"),
},
{
name: "set org metadata",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
orgId: orgId,
key: "key1",
value: "value1",
setupFunc: func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = orgID
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
},
},
assert: func(ttt *assert.CollectT, setMetadataResponse *org.SetOrganizationMetadataResponse, testStartTime time.Time, listMetadataResponse *org.ListOrganizationMetadataResponse) {
assert.WithinRange(ttt, setMetadataResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
integration.AssertMetadataEquals(ttt, expectedMetadata, listMetadataResponse.GetMetadata())
},
},
{
name: "set org metadata on non existant org",
name: "set org metadata on non existent org",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
orgId: "non existant orgid",
key: "key2",
value: "value2",
setupFunc: func(ctx context.Context, _ string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = "wrong-org-id"
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated-value1")))},
},
},
err: errors.New("Organisation not found"),
},
{
name: "update org metadata",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
setupFunc: func() {
_, err := Client.SetOrganizationMetadata(CTX, &org.SetOrganizationMetadataRequest{
OrganizationId: orgId,
setupFunc: func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = orgID
Instance.SetOrganizationMetadata(ctx, orgID, "key1", "value1")
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{
Key: "key3",
Value: []byte("value3"),
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated-value1")))},
},
},
})
require.NoError(t, err)
assert: func(ttt *assert.CollectT, setMetadataResponse *org.SetOrganizationMetadataResponse, testStartTime time.Time, listMetadataResponse *org.ListOrganizationMetadataResponse) {
assert.WithinRange(ttt, setMetadataResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated-value1")))},
}
integration.AssertMetadataEquals(ttt, expectedMetadata, listMetadataResponse.GetMetadata())
},
orgId: orgId,
key: "key4",
value: "value4",
},
{
name: "update org metadata with same value",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
setupFunc: func() {
_, err := Client.SetOrganizationMetadata(CTX, &org.SetOrganizationMetadataRequest{
OrganizationId: orgId,
setupFunc: func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = orgID
Instance.SetOrganizationMetadata(ctx, orgID, "key1", "value1")
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
},
},
assert: func(ttt *assert.CollectT, setMetadataResponse *org.SetOrganizationMetadataResponse, testStartTime time.Time, listMetadataResponse *org.ListOrganizationMetadataResponse) {
assert.True(ttt, setMetadataResponse.GetSetDate().AsTime().Before(testStartTime))
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
integration.AssertMetadataEquals(ttt, expectedMetadata, listMetadataResponse.GetMetadata())
},
},
{
Key: "key5",
Value: []byte("value5"),
name: "delete and update org metadata",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
setupFunc: func(ctx context.Context, orgID string, req *org.SetOrganizationMetadataRequest) {
req.OrganizationId = orgID
Instance.SetOrganizationMetadata(ctx, orgID, "key1", "value1")
Instance.SetOrganizationMetadata(ctx, orgID, "key2", "value2")
},
req: &org.SetOrganizationMetadataRequest{
Metadata: []*org.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated-value1")))}, // updating
{Key: "key2"}, // deleting
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))}, // adding
{Key: "key4"}, // deleting non-existent
},
},
})
require.NoError(t, err)
assert: func(ttt *assert.CollectT, setMetadataResponse *org.SetOrganizationMetadataResponse, testStartTime time.Time, listMetadataResponse *org.ListOrganizationMetadataResponse) {
assert.WithinRange(ttt, setMetadataResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated-value1")))},
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
}
integration.AssertMetadataEquals(ttt, expectedMetadata, listMetadataResponse.GetMetadata())
},
orgId: orgId,
key: "key5",
value: "value5",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
orgResponse := Instance.CreateOrganization(
CTX,
integration.OrganizationName(),
integration.Email(),
)
if tt.setupFunc != nil {
tt.setupFunc()
tt.setupFunc(tt.ctx, orgResponse.OrganizationId, tt.req)
}
got, err := Client.SetOrganizationMetadata(tt.ctx, &org.SetOrganizationMetadataRequest{
OrganizationId: tt.orgId,
Metadata: []*org.Metadata{
{
Key: tt.key,
Value: []byte(tt.value),
},
},
})
testStartTime := time.Now().UTC()
got, err := Client.SetOrganizationMetadata(tt.ctx, tt.req)
if tt.err != nil {
require.Contains(t, err.Error(), tt.err.Error())
return
}
require.NoError(t, err)
// check details
gotCD := got.GetSetDate().AsTime()
now := time.Now()
assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute))
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, 10*time.Minute)
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
// check metadata
listMetadataRes, err := Client.ListOrganizationMetadata(tt.ctx, &org.ListOrganizationMetadataRequest{
OrganizationId: orgId,
OrganizationId: orgResponse.OrganizationId,
})
require.NoError(ttt, err)
foundMetadata := false
foundMetadataKeyCount := 0
for _, res := range listMetadataRes.Metadata {
if res.Key == tt.key {
foundMetadataKeyCount += 1
}
if res.Key == tt.key &&
string(res.Value) == tt.value {
foundMetadata = true
}
}
require.True(ttt, foundMetadata, "unable to find added metadata")
require.Equal(ttt, 1, foundMetadataKeyCount, "same metadata key found multiple times")
tt.assert(ttt, got, testStartTime, listMetadataRes)
}, retryDuration, tick, "timeout waiting for expected organizations being created")
})
}
@@ -1242,7 +1278,7 @@ func TestServer_DeleteOrganizationMetadata(t *testing.T) {
{
name: "delete org metadata for org that does not exist",
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner),
orgId: "non existant org id",
orgId: "non existent org id",
metadataToDelete: []struct{ key, value string }{
{
key: "key4",
@@ -54,7 +54,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
{
@@ -78,7 +78,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
{
@@ -106,7 +106,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
{
@@ -126,7 +126,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
{
@@ -164,7 +164,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
{
@@ -187,7 +187,7 @@ func TestServer_SetUserMetadata(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated")))},
}
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
},
},
}
@@ -3590,7 +3590,7 @@ func TestServer_CreateUser_And_Compare(t *testing.T) {
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
}
assertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
},
}
},
@@ -3627,7 +3627,7 @@ func TestServer_CreateUser_And_Compare(t *testing.T) {
expectedMetadata := []*metadata.Metadata{
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
}
assertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
},
}
},
@@ -3712,7 +3712,7 @@ func TestServer_CreateUser_And_Compare(t *testing.T) {
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
}
assertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, getMetadataResponse.GetMetadata())
},
}
},
@@ -3805,19 +3805,6 @@ func TestServer_CreateUser_And_Compare(t *testing.T) {
}
}
func getMetadataMap(metadata []*metadata.Metadata) map[string][]byte {
metadataByKey := make(map[string][]byte, len(metadata))
for _, md := range metadata {
metadataByKey[md.Key] = md.Value
}
return metadataByKey
}
func assertMetadataEquals(t *testing.T, expected []*metadata.Metadata, actual []*metadata.Metadata) {
assert.Equal(t, len(expected), len(actual))
assert.Equal(t, getMetadataMap(expected), getMetadataMap(actual))
}
func TestServer_CreateUser_Permission(t *testing.T) {
newOrgOwnerEmail := integration.Email()
newOrg := Instance.CreateOrganization(IamCTX, integration.OrganizationName(), newOrgOwnerEmail)
@@ -4615,7 +4602,7 @@ func TestServer_UpdateUser_And_Compare(t *testing.T) {
{Key: "key2", Value: []byte("value2")},
{Key: "key3", Value: []byte("value3")},
}
assertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
},
}
},
@@ -4683,7 +4670,7 @@ func TestServer_UpdateUser_And_Compare(t *testing.T) {
{Key: "key1", Value: []byte("updated_value1")},
{Key: "key4", Value: []byte("value4")},
}
assertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
},
}
},
@@ -4740,7 +4727,7 @@ func TestServer_UpdateUser_And_Compare(t *testing.T) {
{Key: "key2", Value: []byte("value2")},
{Key: "key3", Value: []byte("value3")},
}
assertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
},
}
},
@@ -4802,7 +4789,7 @@ func TestServer_UpdateUser_And_Compare(t *testing.T) {
{Key: "key1", Value: []byte("updated_value1")},
{Key: "key4", Value: []byte("value4")},
}
assertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
integration.AssertMetadataEquals(t, expectedMetadata, listMetadataResponse.GetMetadata())
},
}
},
+32 -4
View File
@@ -1,6 +1,7 @@
package command
import (
"bytes"
"context"
"github.com/zitadel/zitadel/internal/domain"
@@ -46,15 +47,42 @@ func (c *Commands) BulkSetOrgMetadata(ctx context.Context, orgID string, permiss
return nil, err
}
events := make([]eventstore.Command, len(metadatas))
setMetadata := NewOrgMetadataListWriteModel(orgID)
events := make([]eventstore.Command, 0)
setMetadata, err := c.getOrgMetadataListModelByID(ctx, orgID)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&setMetadata.WriteModel)
for i, data := range metadatas {
for _, data := range metadatas {
existingValue, keyExists := setMetadata.metadataList[data.Key]
// if value is empty, a metadata remove event has to be pushed
if len(data.Value) == 0 {
// Ignore deletion if key does not exist
if !keyExists {
continue
}
event := org.NewMetadataRemovedEvent(ctx, orgAgg, data.Key)
events = append(events, event)
continue
}
// if no change to metadata no event has to be pushed
if keyExists && bytes.Equal(existingValue, data.Value) {
continue
}
event, err := c.setOrgMetadata(ctx, orgAgg, data)
if err != nil {
return nil, err
}
events[i] = event
events = append(events, event)
}
// no changes for the metadata
if len(events) == 0 {
return writeModelToObjectDetails(&setMetadata.WriteModel), nil
}
pushedEvents, err := c.eventstore.Push(ctx, events...)
+71 -26
View File
@@ -196,32 +196,6 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
err: zerrors.IsPreconditionFailed,
},
},
{
name: "invalid metadata, pre condition error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"ZITADEL",
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
metadataList: []*domain.Metadata{
{Key: "key"},
{Key: "key1"},
},
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "add metadata, ok",
fields: fields{
@@ -234,6 +208,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
),
),
),
expectFilter(),
expectPush(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
@@ -274,6 +249,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
),
),
),
expectFilter(),
expectPush(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
@@ -303,6 +279,75 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
},
},
},
{
name: "update and delete metadata, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"ZITADEL",
),
),
),
expectFilter(
eventFromEventPusher(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key",
[]byte("value"),
),
),
eventFromEventPusher(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key1",
[]byte("value1"),
),
),
eventFromEventPusher(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key2",
[]byte("value2"),
),
)),
expectPush(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key",
[]byte("updated_value"),
),
org.NewMetadataRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key1",
),
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key3",
[]byte("value3"),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
metadataList: []*domain.Metadata{
{Key: "key", Value: []byte("updated_value")}, // update an existing key
{Key: "key1"}, // delete an existing key
{Key: "key2", Value: []byte("value2")}, // unchanged value
{Key: "key3", Value: []byte("value3")}, // add a new key
{Key: "key4"}, // delete a non-existing key
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "add metadata (no permission), error",
fields: fields{
+1
View File
@@ -76,6 +76,7 @@ func (c *Commands) BulkSetUserMetadata(ctx context.Context, userID, resourceOwne
if err != nil {
return nil, err
}
// no changes for the metadata
if len(events) == 0 {
return writeModelToObjectDetails(&setMetadata.WriteModel), nil
+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),
+3 -3
View File
@@ -263,7 +263,7 @@ service OrganizationService {
// Set Organization Metadata
//
// Adds or updates a metadata value for the requested key. Make sure the value is base64 encoded.
// Sets a list of key value pairs. Existing metadata entries with matching keys are overwritten. Existing metadata entries without matching keys are untouched. To remove existing metadata entries, pass an empty metadata value or use [DeleteOrganizationMetadata](/docs/reference/api/org/zitadel.org.v2.OrganizationService.DeleteOrganizationMetadata). If no metadata entry exists for a given key, passing an empty value for that key has no effect (no-op). For HTTP requests, make sure the bytes array value is base64 encoded.
//
// Required permission:
// - `org.write`
@@ -914,8 +914,8 @@ message Metadata {
// Key is identifier of the metadata entry.
string key = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
// Value is the values of the metadata entry.
bytes value = 2 [(validate.rules).bytes = {min_len: 1, max_len: 500000}];
// Value in the metadata key/value pair. On updates, an empty value will delete the metadata entry.
bytes value = 2 [(validate.rules).bytes = {max_len: 500000}];
}
message SetOrganizationMetadataRequest{
// Organization ID is the unique identifier of the organization whose metadata is to be set.