feat(api): expose swagger licence metadata
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
0ed1cfa1b1
commit
b2d3c96ed7
6 changed files with 86 additions and 15 deletions
39
api.go
39
api.go
|
|
@ -25,19 +25,21 @@ const shutdownTimeout = 10 * time.Second
|
|||
|
||||
// Engine is the central API server managing route groups and middleware.
|
||||
type Engine struct {
|
||||
addr string
|
||||
groups []RouteGroup
|
||||
middlewares []gin.HandlerFunc
|
||||
wsHandler http.Handler
|
||||
sseBroker *SSEBroker
|
||||
swaggerEnabled bool
|
||||
swaggerTitle string
|
||||
swaggerDesc string
|
||||
swaggerVersion string
|
||||
swaggerServers []string
|
||||
pprofEnabled bool
|
||||
expvarEnabled bool
|
||||
graphql *graphqlConfig
|
||||
addr string
|
||||
groups []RouteGroup
|
||||
middlewares []gin.HandlerFunc
|
||||
wsHandler http.Handler
|
||||
sseBroker *SSEBroker
|
||||
swaggerEnabled bool
|
||||
swaggerTitle string
|
||||
swaggerDesc string
|
||||
swaggerVersion string
|
||||
swaggerServers []string
|
||||
swaggerLicenseName string
|
||||
swaggerLicenseURL string
|
||||
pprofEnabled bool
|
||||
expvarEnabled bool
|
||||
graphql *graphqlConfig
|
||||
}
|
||||
|
||||
// New creates an Engine with the given options.
|
||||
|
|
@ -185,7 +187,16 @@ func (e *Engine) build() *gin.Engine {
|
|||
|
||||
// Mount Swagger UI if enabled.
|
||||
if e.swaggerEnabled {
|
||||
registerSwagger(r, e.swaggerTitle, e.swaggerDesc, e.swaggerVersion, e.swaggerServers, e.groups)
|
||||
registerSwagger(
|
||||
r,
|
||||
e.swaggerTitle,
|
||||
e.swaggerDesc,
|
||||
e.swaggerVersion,
|
||||
e.swaggerServers,
|
||||
e.swaggerLicenseName,
|
||||
e.swaggerLicenseURL,
|
||||
e.groups,
|
||||
)
|
||||
}
|
||||
|
||||
// Mount pprof profiling endpoints if enabled.
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ They execute after `gin.Recovery()` but before any route handler. The `Option` t
|
|||
| `WithAuthentik(cfg)` | Authentik forward-auth + OIDC JWT | Permissive; populates context, never rejects |
|
||||
| `WithSwagger(title, desc, ver)` | Swagger UI at `/swagger/` | Runtime spec via `SpecBuilder` |
|
||||
| `WithSwaggerServers(servers...)` | OpenAPI server metadata | Feeds the runtime Swagger spec and exported docs |
|
||||
| `WithSwaggerLicense(name, url)` | OpenAPI licence metadata | Populates the Swagger spec info block without manual `SpecBuilder` wiring |
|
||||
| `WithPprof()` | Go profiling at `/debug/pprof/` | WARNING: do not expose in production without authentication |
|
||||
| `WithExpvar()` | Runtime metrics at `/debug/vars` | WARNING: do not expose in production without authentication |
|
||||
| `WithSecure()` | Security headers | HSTS 1 year, X-Frame-Options DENY, nosniff, strict referrer |
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ func main() {
|
|||
api.WithSecure(),
|
||||
api.WithSlog(nil),
|
||||
api.WithSwagger("My API", "A service description", "1.0.0"),
|
||||
api.WithSwaggerLicense("EUPL-1.2", "https://eupl.eu/1.2/en/"),
|
||||
)
|
||||
|
||||
engine.Register(myRoutes) // any RouteGroup implementation
|
||||
|
|
|
|||
13
options.go
13
options.go
|
|
@ -138,6 +138,19 @@ func WithSwaggerServers(servers ...string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithSwaggerLicense adds licence metadata to the generated Swagger spec.
|
||||
// Pass both a name and URL to populate the OpenAPI info block consistently.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// api.WithSwaggerLicense("EUPL-1.2", "https://eupl.eu/1.2/en/")
|
||||
func WithSwaggerLicense(name, url string) Option {
|
||||
return func(e *Engine) {
|
||||
e.swaggerLicenseName = name
|
||||
e.swaggerLicenseURL = url
|
||||
}
|
||||
}
|
||||
|
||||
// WithPprof enables Go runtime profiling endpoints at /debug/pprof/.
|
||||
// The standard pprof handlers (index, cmdline, profile, symbol, trace,
|
||||
// allocs, block, goroutine, heap, mutex, threadcreate) are registered
|
||||
|
|
|
|||
|
|
@ -40,13 +40,15 @@ func (s *swaggerSpec) ReadDoc() string {
|
|||
}
|
||||
|
||||
// registerSwagger mounts the Swagger UI and doc.json endpoint.
|
||||
func registerSwagger(g *gin.Engine, title, description, version string, servers []string, groups []RouteGroup) {
|
||||
func registerSwagger(g *gin.Engine, title, description, version string, servers []string, licenseName, licenseURL string, groups []RouteGroup) {
|
||||
spec := &swaggerSpec{
|
||||
builder: &SpecBuilder{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Version: version,
|
||||
Servers: servers,
|
||||
LicenseName: licenseName,
|
||||
LicenseURL: licenseURL,
|
||||
},
|
||||
groups: groups,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,6 +258,49 @@ func TestSwagger_Good_InfoFromOptions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSwagger_Good_UsesLicenseMetadata(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
e, err := api.New(
|
||||
api.WithSwagger("Licensed API", "Licensed test", "1.0.0"),
|
||||
api.WithSwaggerLicense("EUPL-1.2", "https://eupl.eu/1.2/en/"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
srv := httptest.NewServer(e.Handler())
|
||||
defer srv.Close()
|
||||
|
||||
resp, err := http.Get(srv.URL + "/swagger/doc.json")
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read body: %v", err)
|
||||
}
|
||||
|
||||
var doc map[string]any
|
||||
if err := json.Unmarshal(body, &doc); err != nil {
|
||||
t.Fatalf("invalid JSON: %v", err)
|
||||
}
|
||||
|
||||
info := doc["info"].(map[string]any)
|
||||
license, ok := info["license"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("expected license metadata in swagger doc")
|
||||
}
|
||||
if license["name"] != "EUPL-1.2" {
|
||||
t.Fatalf("expected license name=%q, got %v", "EUPL-1.2", license["name"])
|
||||
}
|
||||
if license["url"] != "https://eupl.eu/1.2/en/" {
|
||||
t.Fatalf("expected license url=%q, got %v", "https://eupl.eu/1.2/en/", license["url"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwagger_Good_UsesServerMetadata(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue