From b8e8d84ef768595d0914dc467a5b56de39fce65d Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 15 Nov 2023 06:42:35 -0800 Subject: [PATCH] Swagger: Show k8s APIs (#78091) --- .github/CODEOWNERS | 1 - docs/sources/developers/http_api/_index.md | 2 +- pkg/api/README.md | 2 +- pkg/api/api.go | 4 +- pkg/api/openapi3.go | 22 ---- pkg/api/swagger.go | 32 ++++-- public/views/openapi3.html | 63 ----------- public/views/swagger.html | 116 ++++++++++++++------- 8 files changed, 106 insertions(+), 136 deletions(-) delete mode 100644 pkg/api/openapi3.go delete mode 100644 public/views/openapi3.html diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 320982e2fa8..4ee55d32f63 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -467,7 +467,6 @@ cypress.config.js @grafana/grafana-frontend-platform /public/test/ @grafana/grafana-frontend-platform /public/test/helpers/alertingRuleEditor.tsx @grafana/alerting-frontend /public/views/ @grafana/grafana-frontend-platform -/public/views/openapi3.html @grafana/backend-platform /public/views/swagger.html @grafana/backend-platform /public/app/features/explore/Logs/ @grafana/observability-logs diff --git a/docs/sources/developers/http_api/_index.md b/docs/sources/developers/http_api/_index.md index aa01c6d1886..fb7f7ad0718 100644 --- a/docs/sources/developers/http_api/_index.md +++ b/docs/sources/developers/http_api/_index.md @@ -27,7 +27,7 @@ Since version 8.4, HTTP API details are [specified](https://editor.swagger.io/?u Starting from version 9.1, there is also a [OpenAPI v3 specification](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/openapi3.json) (generated by the v2 one). -Users can browser and try out both via the Swagger UI editor (served by the grafana server) by navigating to `/swagger-ui` and `/openapi3` respectively. +Users can browser and try out both via the Swagger UI editor (served by the grafana server) by navigating to `/swagger`. ## Authenticating API requests diff --git a/pkg/api/README.md b/pkg/api/README.md index 63088d9f8de..8a6dd8c23cd 100644 --- a/pkg/api/README.md +++ b/pkg/api/README.md @@ -79,4 +79,4 @@ make swagger-clean && make openapi3-gen They can observe its output into the `public/api-merged.json` and `public/openapi3.json` files. -Finally, they can browser and try out both the OpenAPI v2 and v3 via the Swagger UI editor (served by the grafana server) by navigating to `/swagger-ui` and `/openapi3` respectively. +Finally, they can browser and try out both the OpenAPI v2 and v3 via the Swagger UI editor (served by the grafana server) by navigating to `/swagger`. diff --git a/pkg/api/api.go b/pkg/api/api.go index 550b51de3d9..a81358d1084 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -213,8 +213,8 @@ func (hs *HTTPServer) registerRoutes() { // expose plugin file system assets r.Get("/public/plugins/:pluginId/*", hs.getPluginAssets) - r.Get("/swagger-ui", swaggerUI) - r.Get("/openapi3", openapi3) + // add swagger support + registerSwaggerUI(r) if hs.Features.IsEnabledGlobally(featuremgmt.FlagClientTokenRotation) { r.Post("/api/user/auth-tokens/rotate", routing.Wrap(hs.RotateUserAuthToken)) diff --git a/pkg/api/openapi3.go b/pkg/api/openapi3.go deleted file mode 100644 index f148a5358fa..00000000000 --- a/pkg/api/openapi3.go +++ /dev/null @@ -1,22 +0,0 @@ -package api - -import ( - "net/http" - "strings" - - contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" -) - -func openapi3(c *contextmodel.ReqContext) { - data := map[string]any{ - "Nonce": c.RequestNonce, - } - - // Add CSP for unpkg.com to allow loading of Swagger UI assets - if existingCSP := c.Resp.Header().Get("Content-Security-Policy"); existingCSP != "" { - newCSP := strings.Replace(existingCSP, "style-src", "style-src https://unpkg.com/", 1) - c.Resp.Header().Set("Content-Security-Policy", newCSP) - } - - c.HTML(http.StatusOK, "openapi3", data) -} diff --git a/pkg/api/swagger.go b/pkg/api/swagger.go index 2943bf08557..3212dbc43f3 100644 --- a/pkg/api/swagger.go +++ b/pkg/api/swagger.go @@ -4,19 +4,31 @@ import ( "net/http" "strings" + "github.com/grafana/grafana/pkg/api/routing" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" ) -func swaggerUI(c *contextmodel.ReqContext) { - data := map[string]any{ - "Nonce": c.RequestNonce, - } +func registerSwaggerUI(r routing.RouteRegister) { + // Deprecated + r.Get("/swagger-ui", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "swagger", http.StatusMovedPermanently) + }) + // Deprecated + r.Get("/openapi3", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "swagger?show=v3", http.StatusMovedPermanently) + }) - // Add CSP for unpkg.com to allow loading of Swagger UI assets - if existingCSP := c.Resp.Header().Get("Content-Security-Policy"); existingCSP != "" { - newCSP := strings.Replace(existingCSP, "style-src", "style-src https://unpkg.com/", 1) - c.Resp.Header().Set("Content-Security-Policy", newCSP) - } + r.Get("/swagger", func(c *contextmodel.ReqContext) { + data := map[string]any{ + "Nonce": c.RequestNonce, + } - c.HTML(http.StatusOK, "swagger", data) + // Add CSP for unpkg.com to allow loading of Swagger UI assets + if existingCSP := c.Resp.Header().Get("Content-Security-Policy"); existingCSP != "" { + newCSP := strings.Replace(existingCSP, "style-src", "style-src https://unpkg.com/", 1) + c.Resp.Header().Set("Content-Security-Policy", newCSP) + } + + c.HTML(http.StatusOK, "swagger", data) + }) } diff --git a/public/views/openapi3.html b/public/views/openapi3.html deleted file mode 100644 index 87c1fae1a19..00000000000 --- a/public/views/openapi3.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Swagger UI - - - - - - - -
- - - - - - diff --git a/public/views/swagger.html b/public/views/swagger.html index a4ebd4036e3..a0b5f90727e 100644 --- a/public/views/swagger.html +++ b/public/views/swagger.html @@ -1,15 +1,21 @@ - + - + Swagger UI - - - + + +
- - + + + const urlParams = new URLSearchParams(window.location.search); + const v2 = { name: 'Grafana API (OpenAPI v2)', url: 'public/api-merged.json' }; + const v3 = { name: 'Grafana API (OpenAPI v3)', url: 'public/openapi3.json' }; + const urls = urlParams.get('show') == 'v3' ? [v3, v2] : [v2, v3]; + try { + const rsp = await fetch('openapi/v3'); + const apis = await rsp.json(); + for (const [key, value] of Object.entries(apis.paths)) { + const parts = key.split('/'); + if (parts.length == 3) { + urls.push({ + name: `${parts[1]}/${parts[2]}`, + url: value.serverRelativeURL.substring(1), // remove initial slash + }); + } + } + urls.push({ name: 'Grafana apps (OpenAPI v2)', url: 'openapi/v2' }); + } catch (err) { + // console.warn('Error loading k8s apis', err); + } + + // Begin Swagger UI call region + const ui = SwaggerUIBundle({ + urls, + dom_id: '#swagger-ui', + deepLinking: true, + presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset], + plugins: [SwaggerUIBundle.plugins.DownloadUrl], + layout: 'StandaloneLayout', + filter: true, + tagsSorter: 'alpha', + tryItOutEnabled: true, + }); + + window.ui = ui; + }; +