IAM: Add teams/{id}/groups as a custom endpoint to Teams API (#114228)

* Add teams/{id}/groups as a custom endpoint

* TeamGroupsHandler OSS and Ent registration

* Update OpenAPI spec

* Add indexer tests for external group mapping

* Remove noopsearch

* Remove unnecessary interface declaration, fixes

* Chores

* fmt

* Rename constant

* Align the rest to the changes of main

* Update workspace

* Add missing file
This commit is contained in:
Misi
2025-11-25 14:19:57 +01:00
committed by GitHub
parent 9091ac6f5c
commit 93ec32dd6a
27 changed files with 732 additions and 79 deletions
@@ -62,5 +62,10 @@ func All(sql db.DB, sprinkles DashboardStats) ([]resource.DocumentBuilderInfo, e
return nil, err
}
return []resource.DocumentBuilderInfo{dashboards, users}, nil
extGroupMappings, err := GetExternalGroupMappingBuilder()
if err != nil {
return nil, err
}
return []resource.DocumentBuilderInfo{dashboards, users, extGroupMappings}, nil
}
@@ -58,6 +58,18 @@ func TestUserDocumentBuilder(t *testing.T) {
})
}
func TestExternalGroupMappingDocumentBuilder(t *testing.T) {
info, err := GetExternalGroupMappingBuilder()
require.NoError(t, err)
doSnapshotTests(t, info.Builder, "external_group_mapping", &resourcepb.ResourceKey{
Namespace: "default",
Group: "iam.grafana.app",
Resource: "externalgroupmappings",
}, []string{
"mapping-with-team-and-group",
})
}
func TestDashboardDocumentBuilder(t *testing.T) {
key := &resourcepb.ResourceKey{
Namespace: "default",
@@ -0,0 +1,80 @@
package builders
import (
"bytes"
"context"
"encoding/json"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
)
const (
EXTERNAL_GROUP_MAPPING_TEAM = "team"
EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP = "external_group"
)
var ExternalGroupMappingTableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
EXTERNAL_GROUP_MAPPING_TEAM: {
Name: EXTERNAL_GROUP_MAPPING_TEAM,
Type: resourcepb.ResourceTableColumnDefinition_STRING,
Description: "The team name associated with the external group mapping",
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
Filterable: true,
},
},
EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP: {
Name: EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP,
Type: resourcepb.ResourceTableColumnDefinition_STRING,
Description: "The external group name/id associated with the external group mapping",
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
Filterable: true,
},
},
}
func GetExternalGroupMappingBuilder() (resource.DocumentBuilderInfo, error) {
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(ExternalGroupMappingTableColumnDefinitions))
for _, v := range ExternalGroupMappingTableColumnDefinitions {
values = append(values, v)
}
fields, err := resource.NewSearchableDocumentFields(values)
return resource.DocumentBuilderInfo{
GroupResource: iamv0.ExternalGroupMappingResourceInfo.GroupResource(),
Fields: fields,
Builder: new(extGroupMappingDocumentBuilder),
}, err
}
var _ resource.DocumentBuilder = new(extGroupMappingDocumentBuilder)
type extGroupMappingDocumentBuilder struct{}
// BuildDocument implements resource.DocumentBuilder.
func (u *extGroupMappingDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
extGroupMapping := &iamv0.ExternalGroupMapping{}
err := json.NewDecoder(bytes.NewReader(value)).Decode(extGroupMapping)
if err != nil {
return nil, err
}
obj, err := utils.MetaAccessor(extGroupMapping)
if err != nil {
return nil, err
}
doc := resource.NewIndexableDocument(key, rv, obj)
doc.Fields = make(map[string]any)
if extGroupMapping.Spec.TeamRef.Name != "" {
doc.Fields[EXTERNAL_GROUP_MAPPING_TEAM] = extGroupMapping.Spec.TeamRef.Name
}
if extGroupMapping.Spec.ExternalGroupId != "" {
doc.Fields[EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP] = extGroupMapping.Spec.ExternalGroupId
}
return doc, nil
}
@@ -0,0 +1,17 @@
{
"key": {
"namespace": "default",
"group": "iam.grafana.app",
"resource": "externalgroupmappings",
"name": "mapping-with-team-and-group"
},
"name": "mapping-with-team-and-group",
"rv": 1234,
"fields": {
"team": "team1",
"external_group": "group1"
},
"title": "mapping-with-team-and-group",
"title_ngram": "mapping-with-team-and-group",
"title_phrase": "mapping-with-team-and-group"
}
@@ -0,0 +1,14 @@
{
"apiVersion": "iam.grafana.app/v0alpha1",
"kind": "ExternalGroupMapping",
"metadata": {
"name": "mapping-with-team-and-group",
"namespace": "default"
},
"spec": {
"teamRef": {
"name": "team1"
},
"externalGroupId": "group1"
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ const (
USER_LOGIN = "login"
)
var TableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
var UserTableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
USER_EMAIL: {
Name: USER_EMAIL,
Type: resourcepb.ResourceTableColumnDefinition_STRING,
@@ -38,8 +38,8 @@ var TableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinitio
}
func GetUserBuilder() (resource.DocumentBuilderInfo, error) {
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(TableColumnDefinitions))
for _, v := range TableColumnDefinitions {
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(UserTableColumnDefinitions))
for _, v := range UserTableColumnDefinitions {
values = append(values, v)
}
fields, err := resource.NewSearchableDocumentFields(values)