feat(api): document debug endpoints in OpenAPI spec
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
d803ac8f3b
commit
41615bbe47
3 changed files with 135 additions and 0 deletions
125
openapi.go
125
openapi.go
|
|
@ -35,6 +35,8 @@ type SpecBuilder struct {
|
|||
LicenseURL string
|
||||
ExternalDocsDescription string
|
||||
ExternalDocsURL string
|
||||
PprofEnabled bool
|
||||
ExpvarEnabled bool
|
||||
}
|
||||
|
||||
type preparedRouteGroup struct {
|
||||
|
|
@ -197,6 +199,14 @@ func (sb *SpecBuilder) buildPaths(groups []preparedRouteGroup) map[string]any {
|
|||
paths[ssePath] = ssePathItem(ssePath, operationIDs)
|
||||
}
|
||||
|
||||
if sb.PprofEnabled {
|
||||
paths["/debug/pprof"] = pprofPathItem(operationIDs)
|
||||
}
|
||||
|
||||
if sb.ExpvarEnabled {
|
||||
paths["/debug/vars"] = expvarPathItem(operationIDs)
|
||||
}
|
||||
|
||||
for _, g := range groups {
|
||||
for _, rd := range g.descs {
|
||||
fullPath := joinOpenAPIPath(g.group.BasePath(), rd.Path)
|
||||
|
|
@ -598,6 +608,14 @@ func (sb *SpecBuilder) buildTags(groups []preparedRouteGroup) []map[string]any {
|
|||
seen["events"] = true
|
||||
}
|
||||
|
||||
if (sb.PprofEnabled || sb.ExpvarEnabled) && !seen["debug"] {
|
||||
tags = append(tags, map[string]any{
|
||||
"name": "debug",
|
||||
"description": "Runtime debug endpoints",
|
||||
})
|
||||
seen["debug"] = true
|
||||
}
|
||||
|
||||
for _, g := range groups {
|
||||
name := strings.TrimSpace(g.group.Name())
|
||||
if name != "" && !seen[name] {
|
||||
|
|
@ -703,6 +721,113 @@ func ssePathItem(path string, operationIDs map[string]int) map[string]any {
|
|||
}
|
||||
}
|
||||
|
||||
func pprofPathItem(operationIDs map[string]int) map[string]any {
|
||||
return map[string]any{
|
||||
"get": map[string]any{
|
||||
"summary": "pprof index",
|
||||
"description": "Lists the available Go runtime profiles",
|
||||
"tags": []string{"debug"},
|
||||
"operationId": operationID("get", "/debug/pprof", operationIDs),
|
||||
"security": []any{
|
||||
map[string]any{
|
||||
"bearerAuth": []any{},
|
||||
},
|
||||
},
|
||||
"responses": map[string]any{
|
||||
"200": map[string]any{
|
||||
"description": "pprof index",
|
||||
"content": map[string]any{
|
||||
"text/html": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
"401": map[string]any{
|
||||
"description": "Unauthorised",
|
||||
"content": map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
"403": map[string]any{
|
||||
"description": "Forbidden",
|
||||
"content": map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func expvarPathItem(operationIDs map[string]int) map[string]any {
|
||||
return map[string]any{
|
||||
"get": map[string]any{
|
||||
"summary": "Runtime metrics",
|
||||
"description": "Returns expvar metrics as JSON",
|
||||
"tags": []string{"debug"},
|
||||
"operationId": operationID("get", "/debug/vars", operationIDs),
|
||||
"security": []any{
|
||||
map[string]any{
|
||||
"bearerAuth": []any{},
|
||||
},
|
||||
},
|
||||
"responses": map[string]any{
|
||||
"200": map[string]any{
|
||||
"description": "Runtime metrics",
|
||||
"content": map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
"401": map[string]any{
|
||||
"description": "Unauthorised",
|
||||
"content": map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
"403": map[string]any{
|
||||
"description": "Forbidden",
|
||||
"content": map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"schema": map[string]any{
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": standardResponseHeaders(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func graphqlRequestSchema() map[string]any {
|
||||
return map[string]any{
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ func (e *Engine) OpenAPISpecBuilder() *SpecBuilder {
|
|||
if e.sseBroker != nil {
|
||||
builder.SSEPath = resolveSSEPath(e.ssePath)
|
||||
}
|
||||
builder.PprofEnabled = e.pprofEnabled
|
||||
builder.ExpvarEnabled = e.expvarEnabled
|
||||
|
||||
return builder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ func TestEngine_Good_OpenAPISpecBuilderCarriesEngineMetadata(t *testing.T) {
|
|||
api.WithGraphQL(newTestSchema(), api.WithGraphQLPath("/gql")),
|
||||
api.WithSSE(broker),
|
||||
api.WithSSEPath("/events"),
|
||||
api.WithPprof(),
|
||||
api.WithExpvar(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
|
@ -107,4 +109,10 @@ func TestEngine_Good_OpenAPISpecBuilderCarriesEngineMetadata(t *testing.T) {
|
|||
if _, ok := paths["/events"]; !ok {
|
||||
t.Fatal("expected SSE path from engine metadata in generated spec")
|
||||
}
|
||||
if _, ok := paths["/debug/pprof"]; !ok {
|
||||
t.Fatal("expected pprof path from engine metadata in generated spec")
|
||||
}
|
||||
if _, ok := paths["/debug/vars"]; !ok {
|
||||
t.Fatal("expected expvar path from engine metadata in generated spec")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue