IAM: Add search for teams in app platform (#113503)

* add legacy search (wip)

* fix search field name

* implement team search endpoint

* generate openapi spec

* generate endpoints for frontend

* minor fixes

* fix issues found while testing

* add more fields to search result

* add basic unit tests

* add more unit tests

* improve getColumns() func in legacy search

* configure search endpoint in team.cue

* add team search handler

* add the searchTeams endpoint to manifest.cue

* make gofmt

* update openapi spec

* generate frontend endpoints

* remove unused field

* move fields defiitions to separate builder

* fix legacy search

* fix unit tests

* fix unit test

* address feedback

* fix unit test

* update openapi specs

* yarn generate-apis

* add missing unit tests
This commit is contained in:
Mihai Doarna
2025-12-08 15:02:59 +02:00
committed by GitHub
parent 5bd73f3264
commit b2e1b257b3
27 changed files with 1957 additions and 15 deletions
@@ -67,5 +67,10 @@ func All(sql db.DB, sprinkles DashboardStats) ([]resource.DocumentBuilderInfo, e
return nil, err
}
return []resource.DocumentBuilderInfo{dashboards, users, extGroupMappings}, nil
teams, err := GetTeamSearchBuilder()
if err != nil {
return nil, err
}
return []resource.DocumentBuilderInfo{dashboards, users, extGroupMappings, teams}, nil
}
@@ -70,6 +70,18 @@ func TestExternalGroupMappingDocumentBuilder(t *testing.T) {
})
}
func TestTeamSearchBuilder(t *testing.T) {
info, err := GetTeamSearchBuilder()
require.NoError(t, err)
doSnapshotTests(t, info.Builder, "team", &resourcepb.ResourceKey{
Namespace: "default",
Group: "iam.grafana.app",
Resource: "searchTeams",
}, []string{
"with-email-and-external-uid",
})
}
func TestDashboardDocumentBuilder(t *testing.T) {
key := &resourcepb.ResourceKey{
Namespace: "default",
@@ -0,0 +1,87 @@
package builders
import (
"bytes"
"context"
"encoding/json"
"k8s.io/apimachinery/pkg/runtime/schema"
"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 (
TEAM_SEARCH_EMAIL = "email"
TEAM_SEARCH_PROVISIONED = "provisioned"
TEAM_SEARCH_EXTERNAL_UID = "externalUID"
)
var TeamSearchTableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
TEAM_SEARCH_EMAIL: {
Name: TEAM_SEARCH_EMAIL,
Type: resourcepb.ResourceTableColumnDefinition_STRING,
Description: "Email of the team",
},
TEAM_SEARCH_PROVISIONED: {
Name: TEAM_SEARCH_PROVISIONED,
Type: resourcepb.ResourceTableColumnDefinition_BOOLEAN,
Description: "Whether the team is provisioned",
},
TEAM_SEARCH_EXTERNAL_UID: {
Name: TEAM_SEARCH_EXTERNAL_UID,
Type: resourcepb.ResourceTableColumnDefinition_STRING,
Description: "External UID of the team",
},
}
func GetTeamSearchBuilder() (resource.DocumentBuilderInfo, error) {
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(TeamSearchTableColumnDefinitions))
for _, v := range TeamSearchTableColumnDefinitions {
values = append(values, v)
}
fields, err := resource.NewSearchableDocumentFields(values)
return resource.DocumentBuilderInfo{
GroupResource: schema.GroupResource{
Group: "iam.grafana.app",
Resource: "searchTeams",
},
Fields: fields,
Builder: new(teamSearchBuilder),
}, err
}
var _ resource.DocumentBuilder = new(teamSearchBuilder)
type teamSearchBuilder struct{}
func (t *teamSearchBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
team := &v0alpha1.Team{}
err := json.NewDecoder(bytes.NewReader(value)).Decode(team)
if err != nil {
return nil, err
}
obj, err := utils.MetaAccessor(team)
if err != nil {
return nil, err
}
doc := resource.NewIndexableDocument(key, rv, obj)
doc.Fields = make(map[string]any)
if team.Spec.Email != "" {
doc.Fields[TEAM_SEARCH_EMAIL] = team.Spec.Email
}
if team.Spec.Provisioned {
doc.Fields[TEAM_SEARCH_PROVISIONED] = team.Spec.Provisioned
}
if team.Spec.ExternalUID != "" {
doc.Fields[TEAM_SEARCH_EXTERNAL_UID] = team.Spec.ExternalUID
}
return doc, nil
}
@@ -0,0 +1,18 @@
{
"key": {
"namespace": "default",
"group": "iam.grafana.app",
"resource": "searchTeams",
"name": "with-email-and-external-uid"
},
"name": "with-email-and-external-uid",
"rv": 1234,
"title": "Team With Email And External UID",
"title_ngram": "Team With Email And External UID",
"title_phrase": "team with email and external uid",
"fields": {
"email": "test@example.com",
"externalUID": "external-uid",
"provisioned": true
}
}
@@ -0,0 +1,13 @@
{
"apiVersion": "iam.grafana.app/v0alpha1",
"kind": "Team",
"metadata": {
"name": "team-with-email-and-external-uid"
},
"spec": {
"title": "Team With Email And External UID",
"email": "test@example.com",
"provisioned": true,
"externalUID": "external-uid"
}
}