diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go index 2789d859347..9112101033e 100644 --- a/pkg/registry/apis/iam/register.go +++ b/pkg/registry/apis/iam/register.go @@ -2,6 +2,7 @@ package iam import ( "context" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -10,6 +11,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" genericapiserver "k8s.io/apiserver/pkg/server" common "k8s.io/kube-openapi/pkg/common" + "k8s.io/kube-openapi/pkg/spec3" "k8s.io/kube-openapi/pkg/validation/spec" "github.com/grafana/authlib/types" @@ -128,6 +130,58 @@ func (b *IdentityAccessManagementAPIBuilder) GetOpenAPIDefinitions() common.GetO return iamv0.GetOpenAPIDefinitions } +func (b *IdentityAccessManagementAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) { + oas.Info.Description = "Identity and Access Management" + + defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} }) + defsBase := "github.com/grafana/grafana/pkg/apis/iam/v0alpha1." + + // Add missing schemas + for k, v := range defs { + clean := strings.Replace(k, defsBase, "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.", 1) + if oas.Components.Schemas[clean] == nil { + oas.Components.Schemas[clean] = &v.Schema + } + } + compBase := "com.github.grafana.grafana.pkg.apis.iam.v0alpha1." + schema := oas.Components.Schemas[compBase+"DisplayList"].Properties["display"] + schema.Items = &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + AllOf: []spec.Schema{ + { + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/components/schemas/" + compBase + "Display"), + }, + }, + }, + }, + }, + } + oas.Components.Schemas[compBase+"DisplayList"].Properties["display"] = schema + oas.Components.Schemas[compBase+"DisplayList"].Properties["metadata"] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + AllOf: []spec.Schema{ + { + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta"), + }, + }, + }}, + } + oas.Components.Schemas[compBase+"Display"].Properties["identity"] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + AllOf: []spec.Schema{ + { + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/components/schemas/" + compBase + "IdentityRef"), + }, + }, + }}, + } + return oas, nil +} + func (b *IdentityAccessManagementAPIBuilder) GetAPIRoutes() *builder.APIRoutes { defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} }) return b.display.GetAPIRoutes(defs) diff --git a/pkg/registry/apis/iam/user/rest_display.go b/pkg/registry/apis/iam/user/rest_display.go index c82567878c7..b1b4232005e 100644 --- a/pkg/registry/apis/iam/user/rest_display.go +++ b/pkg/registry/apis/iam/user/rest_display.go @@ -29,11 +29,6 @@ func NewLegacyDisplayREST(store legacy.LegacyIdentityStore) *LegacyDisplayREST { } func (r *LegacyDisplayREST) GetAPIRoutes(defs map[string]common.OpenAPIDefinition) *builder.APIRoutes { - listSchema := defs["github.com/grafana/grafana/pkg/apis/iam/v0alpha1.DisplayList"].Schema - displaySchema := defs["github.com/grafana/grafana/pkg/apis/iam/v0alpha1.Display"].Schema - identitySchema := defs["github.com/grafana/grafana/pkg/apis/iam/v0alpha1.IdentityRef"].Schema - listSchema.Properties["display"].Items.Schema = &displaySchema // not sure why this is lost - displaySchema.Properties["identity"] = identitySchema // not sure why this is lost return &builder.APIRoutes{ Namespace: []builder.APIRouteHandler{ { @@ -76,7 +71,11 @@ func (r *LegacyDisplayREST) GetAPIRoutes(defs map[string]common.OpenAPIDefinitio Content: map[string]*spec3.MediaType{ "application/json": { MediaTypeProps: spec3.MediaTypeProps{ - Schema: &listSchema, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/components/schemas/com.github.grafana.grafana.pkg.apis.iam.v0alpha1.DisplayList"), + }, + }, }, }, }, diff --git a/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json index 4ad7f837fdb..f6bb170a1c2 100644 --- a/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json @@ -1,6 +1,7 @@ { "openapi": "3.0.0", "info": { + "description": "Identity and Access Management", "title": "iam.grafana.app/v0alpha1" }, "paths": { @@ -73,89 +74,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "keys", - "display" - ], - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "display": { - "description": "Matching items (the caller may need to remap from keys to results)", - "type": "array", - "items": { - "type": "object", - "required": [ - "identity", - "displayName" - ], - "properties": { - "avatarURL": { - "description": "AvatarURL is the url where we can get the avatar for identity", - "type": "string" - }, - "displayName": { - "description": "Display name for identity.", - "type": "string", - "default": "" - }, - "identity": { - "type": "object", - "required": [ - "type", - "name" - ], - "properties": { - "name": { - "description": "Name is the unique identifier for identity, guaranteed to be a unique value for the type within a namespace.", - "type": "string", - "default": "" - }, - "type": { - "description": "Type of identity e.g. \"user\". For a full list see https://github.com/grafana/authlib/blob/d6737a7dc8f55e9d42834adb83b5da607ceed293/types/type.go#L15", - "type": "string", - "default": "" - } - } - }, - "internalId": { - "description": "InternalID is the legacy numeric id for identity, Deprecated: use the identityRef where possible", - "type": "integer", - "format": "int64" - } - } - }, - "x-kubernetes-list-type": "atomic" - }, - "invalidKeys": { - "description": "Input keys that were not useable", - "type": "array", - "items": { - "type": "string", - "default": "" - }, - "x-kubernetes-list-type": "set" - }, - "keys": { - "description": "Request keys used to lookup the display value", - "type": "array", - "items": { - "type": "string", - "default": "" - }, - "x-kubernetes-list-type": "set" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "default": {} - } - } + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.iam.v0alpha1.DisplayList" } } } @@ -2511,6 +2430,90 @@ "additionalProperties": true, "x-kubernetes-preserve-unknown-fields": true }, + "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.Display": { + "type": "object", + "required": [ + "identity", + "displayName" + ], + "properties": { + "avatarURL": { + "description": "AvatarURL is the url where we can get the avatar for identity", + "type": "string" + }, + "displayName": { + "description": "Display name for identity.", + "type": "string", + "default": "" + }, + "identity": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.iam.v0alpha1.IdentityRef" + } + ] + }, + "internalId": { + "description": "InternalID is the legacy numeric id for identity, Deprecated: use the identityRef where possible", + "type": "integer", + "format": "int64" + } + } + }, + "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.DisplayList": { + "type": "object", + "required": [ + "keys", + "display" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "display": { + "description": "Matching items (the caller may need to remap from keys to results)", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.iam.v0alpha1.Display" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "invalidKeys": { + "description": "Input keys that were not useable", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "set" + }, + "keys": { + "description": "Request keys used to lookup the display value", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "set" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + } + }, "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.IdentityRef": { "type": "object", "required": [ @@ -2734,6 +2737,45 @@ } } }, + "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.ServiceAccountToken": { + "type": "object", + "required": [ + "created" + ], + "properties": { + "created": {}, + "expires": {}, + "lastUsed": {}, + "name": { + "type": "string" + }, + "revoked": { + "type": "boolean" + } + } + }, + "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.ServiceAccountTokenList": { + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {} + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {} + } + } + }, "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.Team": { "type": "object", "properties": { diff --git a/public/app/features/explore/QueryLibrary/utils/dataFetching.ts b/public/app/features/explore/QueryLibrary/utils/dataFetching.ts index 03eea720d62..e874d6a331e 100644 --- a/public/app/features/explore/QueryLibrary/utils/dataFetching.ts +++ b/public/app/features/explore/QueryLibrary/utils/dataFetching.ts @@ -7,17 +7,16 @@ import { getDataSourceSrv } from '@grafana/runtime'; import { DataQuery, DataSourceRef } from '@grafana/schema'; import { createQueryText } from '../../../../core/utils/richHistory'; -import { useGetDisplayListQuery } from '../../../iam'; +import { useGetDisplayMappingQuery } from '../../../iam'; import { getDatasourceSrv } from '../../../plugins/datasource_srv'; import { QueryTemplate } from '../../../query-library/types'; export function useLoadUsers(userUIDs: string[] | undefined) { const userQtList = uniq(compact(userUIDs)); - const usersParam = userQtList.map((userUid) => `key=${encodeURIComponent(userUid)}`).join('&'); - return useGetDisplayListQuery( + return useGetDisplayMappingQuery( userUIDs ? { - name: `name?${usersParam}`, + key: userQtList, } : skipToken ); diff --git a/public/app/features/iam/api/endpoints.gen.ts b/public/app/features/iam/api/endpoints.gen.ts index 5a9acf6c123..9f424e5ec09 100644 --- a/public/app/features/iam/api/endpoints.gen.ts +++ b/public/app/features/iam/api/endpoints.gen.ts @@ -1,28 +1,33 @@ import { iamApi as api } from './api'; -export const addTagTypes = ['DisplayList'] as const; +export const addTagTypes = ['Display'] as const; const injectedRtkApi = api .enhanceEndpoints({ addTagTypes, }) .injectEndpoints({ endpoints: (build) => ({ - getDisplayList: build.query({ - query: (queryArg) => ({ url: `/display/${queryArg.name}` }), - providesTags: ['DisplayList'], + getDisplayMapping: build.query({ + query: (queryArg) => ({ + url: `/display`, + params: { + key: queryArg.key, + }, + }), + providesTags: ['Display'], }), }), overrideExisting: false, }); export { injectedRtkApi as generatedIamApi }; -export type GetDisplayListApiResponse = /** status 200 OK */ DisplayList; -export type GetDisplayListApiArg = { - /** name of the DisplayList */ - name: string; +export type GetDisplayMappingApiResponse = /** status 200 undefined */ DisplayList; +export type GetDisplayMappingApiArg = { + /** Display keys */ + key: string[]; }; export type IdentityRef = { - /** Name is the unique identifier for identity, guaranteed jo be a unique value for the type within a namespace. */ + /** Name is the unique identifier for identity, guaranteed to be a unique value for the type within a namespace. */ name: string; - /** Type of identity e.g. "user". For a full list see https://github.com/grafana/authlib/blob/2f8d13a83ca3e82da08b53726de1697ee5b5b4cc/claims/type.go#L15-L24 */ + /** Type of identity e.g. "user". For a full list see https://github.com/grafana/authlib/blob/d6737a7dc8f55e9d42834adb83b5da607ceed293/types/type.go#L15 */ type: string; }; export type Display = { @@ -31,7 +36,7 @@ export type Display = { /** Display name for identity. */ displayName: string; identity: IdentityRef; - /** InternalID is the legacy numreric id for identity, this is deprecated and should be phased out */ + /** InternalID is the legacy numeric id for identity, Deprecated: use the identityRef where possible */ internalId?: number; }; export type ListMeta = { diff --git a/public/app/features/iam/api/scripts/generate-rtk-apis.ts b/public/app/features/iam/api/scripts/generate-rtk-apis.ts index 60c8affe5a0..238d0f6bc75 100644 --- a/public/app/features/iam/api/scripts/generate-rtk-apis.ts +++ b/public/app/features/iam/api/scripts/generate-rtk-apis.ts @@ -28,7 +28,7 @@ const config: ConfigFile = { '../endpoints.gen.ts': { apiFile: '../api.ts', apiImport: 'iamApi', - filterEndpoints: ['getDisplayList'], + filterEndpoints: ['getDisplayMapping'], exportName: 'generatedIamApi', flattenArg: false, }, diff --git a/public/app/features/iam/index.ts b/public/app/features/iam/index.ts index ff22af53364..5236538f8dd 100644 --- a/public/app/features/iam/index.ts +++ b/public/app/features/iam/index.ts @@ -1,3 +1,3 @@ import { generatedIamApi } from './api/endpoints.gen'; -export const { useGetDisplayListQuery } = generatedIamApi; +export const { useGetDisplayMappingQuery } = generatedIamApi;