Implementing OpenApi 3 specs (converted from Swagger 2) (#53243)
* Adding OpenApi target to makefile, to covert swagger spec into OpenAPI 3 * Adding endpoint to server swaggerui with new openapi3 specs * Passing output file as parameter for OpenApi3 specs * Implement workaround for missing host and prefix domain
This commit is contained in:
1 parent
138f03aad1
commit
e8e7b59bb0
7 files changed
+145
-2
No files matched your search
@@ -29,3 +29,4 @@ theme.dark.generated.json
|
||||
# Generated Swagger API specs
|
||||
public/api-spec.json
|
||||
public/api-merged.json
|
||||
public/openapi3.json
|
||||
@@ -54,10 +54,15 @@ validate-api-spec: $(MERGED_SPEC_TARGET) $(SWAGGER) ## Validate API spec
|
||||
$(SWAGGER) validate $(<)
|
||||
|
||||
clean-api-spec:
|
||||
rm $(SPEC_TARGET) $(MERGED_SPEC_TARGET)
|
||||
rm $(SPEC_TARGET) $(MERGED_SPEC_TARGET) $(OAPI_SPEC_TARGET)
|
||||
|
||||
##@ OpenAPI 3
|
||||
OAPI_SPEC_TARGET = public/openapi3.json
|
||||
|
||||
openapi3-gen: swagger-api-spec ## Generates OpenApi 3 specs from the Swagger 2 already generated
|
||||
$(GO) run scripts/openapi3/openapi3conv.go $(MERGED_SPEC_TARGET) $(OAPI_SPEC_TARGET)
|
||||
|
||||
##@ Building
|
||||
|
||||
gen-cue: ## Do all CUE/Thema code generation
|
||||
@echo "generate code from .cue files"
|
||||
go generate ./pkg/framework/coremodel
|
||||
|
||||
@@ -187,6 +187,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagSwaggerUi) {
|
||||
r.Get("/swagger-ui", swaggerUI)
|
||||
r.Get("/openapi3", openapi3)
|
||||
}
|
||||
|
||||
// authed api
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func openapi3(c *models.ReqContext) {
|
||||
c.HTML(http.StatusOK, "openapi3", nil)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"components":{"responses":{"SMTPNotEnabledError":{"description":"(empty)"},"acceptedResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseBody"}}},"description":"AcceptedResponse"},"addPermissionResponse":{"content":{"application/json":{"schema":{"properties":{"message":{"type":"string"},"permissionId":{"format":"int64","type":"integer"}},"type":"object"}}},"description":"(empty)"},"adminCreateUserResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserIdDTO"}}},"description":"(empty)"},"adminGetSettingsResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsBag"}}},"description":"(empty)"},"adminGetStatsResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminStats"}}},"description":"(empty)"},"adminGetUserAuthTokensResponse":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserToken"},"type":"array"}}},"description":"(empty)"},"badRequestError":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseBody"}}},"description":"BadRequestError is returned when the request is invalid and it cannot be processed."},"calculateDashboardDiffResponse":{"content":{"application/json":{"schema":{"items":{"format":"uint8","type":"integer"},"type":"array"}}},"description":"(empty)"},"conflictError":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseBody"}}},"description":"ConflictError"},"contentResponse":{"content":{"application/json":{"schema":{"items":{"format":"uint8","type":"integer"},"type":"array"}}},"description":"(empty)"},"createCorrelationResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCorrelationResponseBody"}}},"description":"(empty)"},"createDashboardSnapshotResponse":{"content":{"application/json":{"schema":{"properties":{"deleteKey":{"description":"Unique key used to delete the snapshot. It is different from the key so that only the creator can delete the snapshot.","type":"string"},"deleteUrl":{"type":"string"},"id":{"description":"Snapshot id","format":"int64","type":"integer"},"key":{"description":"Unique key","type":"string"},"url":{"type":"string"}},"type":"object"}}},"description":"(empty)"},"createOrUpdateDatasourceResponse":{"content":{"application/json":{"schema":{"properties":{"datasource":{"$ref":"#/components/schemas/DataSource"},"id":{"description":"ID Identifier of the new data source.","example":65,"format":"int64","type":"integer"},"message":{"description":"Message Message of the deleted dashboard.","example":"Data source added","type":"string"},"name":{"description":"Name of the new data source.","example":"My Data source","type":"string"}},"required":["id","name","message","datasource"],"type":"object"}}},"description":"(empty)"},"createOrgResponse":{"content":{"application/json":{"schema":{"properties":{"message":{"description":"Message Message of the created org.","example":"Data source added","type":"string"},"orgId":{"description":"ID Identifier of the created org.","example":65,"format":"int64","type":"integer"}},"required":["orgId","message"],"type":"object"}}},"description":"(empty)"},"createPlaylistResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Playlist"}}},"description":"(empty)"},"createReportResponse":{"content":{"application/json":{"schema":{"properties":{"id":{"format":"int64","type":"integer"},"message":{"type":"string"}},"type":"object"}}},"description":"(empty)"},"createRoleResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDTO"}}},"description":"(empty)"},"createServiceAccountResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountDTO"}}},"description":"(empty)"},"createTeamResponse":{"content":{"application/json":{"schema":{"properties":{"message":{"type":"string"},"teamId":{"format":"int64","type":"integer"}},"type":"object"}}},"description":"(empty)"},"createTokenResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewApiKeyResult"}}},"description":"(empty)"},"dashboardResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardFullWithMeta"}}},"description":"(empty)"},"dashboardVersionResponse":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVersionMeta"}}},"description":"(empty)"},"dashboardVersionsResponse":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DashboardVersionDTO"},"type":"array"}}},"description":"(empty)"},"deleteAlertNotificationChannelResponse":{"content":{"application/json":{"schema":{"properties":{"id":{"description":"ID Identifier of the deleted notification channel.","example":65,"format":"int64","type":"integer"},"message":{"description":"Message Message of the deleted notificatiton channel.","type":"string"}},"required":["id","message"],"type":"object"}}},"descriLine truncated
|
||||
@@ -0,0 +1,63 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.3.0/swagger-ui.css" integrity="sha384-pzdBB6iZwPIzBHgXle+9cgvKuMgtWNrBopXkjrWnKCi3m4uJsPPdLQ4IPMqRDirS" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="https://unpkg.com/swagger-ui-dist@4.3.0/swagger-ui-bundle.js" charset="UTF-8" integrity="sha384-BGJ5JzR5LEl4ETmxXXlZtXtMWj3uQ9jj9/OHe3yrn5rrtAyLOz1SyyzwMfuwZgPc" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@4.3.0/swagger-ui-standalone-preset.js" charset="UTF-8" integrity="sha384-AWSfISmlS8fS336GXRkpL0Uv6EbCpsFfXDUwmklhbb3SctGSuvXWBcbjERjgf/e4" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "/public/openapi3.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout",
|
||||
filter: true,
|
||||
tagsSorter: "alpha",
|
||||
tryItOutEnabled: true
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/getkin/kin-openapi/openapi2"
|
||||
"github.com/getkin/kin-openapi/openapi2conv"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// main This simple script will take the swagger v2 spec generated by grafana and convert them into openapi 3
|
||||
// saving them as new json file to be able lo load and show
|
||||
// The first parameter, if present, will be the input file
|
||||
// The second parameter, if present, will be the output file
|
||||
func main() {
|
||||
outFile := "public/openapi3.json"
|
||||
inFile := "api-merged.json"
|
||||
args := os.Args[1:]
|
||||
|
||||
// first parameter as the input
|
||||
if len(args) > 0 && args[0] != "" {
|
||||
inFile = args[0]
|
||||
}
|
||||
|
||||
// second parameter as output
|
||||
if len(args) > 1 && args[1] != "" {
|
||||
outFile = args[1]
|
||||
}
|
||||
|
||||
fmt.Printf("Reading swagger 2 file %s\n", inFile)
|
||||
byt, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var doc2 openapi2.T
|
||||
if err = json.Unmarshal(byt, &doc2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
doc3, err := openapi2conv.ToV3(&doc2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// this is a workaround. In the swagger2 specs there ir no definition of the host, so the converter can not create
|
||||
// a URL. Adding this will ensure that all the api calls start with "/api".
|
||||
doc3.AddServer(&openapi3.Server{URL: "/api"})
|
||||
|
||||
j3, err := doc3.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(outFile, j3, 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("OpenAPI specs generated in file %s\n", outFile)
|
||||
}
|
||||
Reference in new issue
Block a user