From 8a23545a671f59c62345da9e059723caaea6823b Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 06:38:05 +0000 Subject: [PATCH] feat(api): expose transport config snapshot Co-Authored-By: Virgil --- spec_builder_helper.go | 25 ++++++------------- spec_builder_helper_test.go | 43 +++++++++++++++++++++++++++++++ transport.go | 50 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 transport.go diff --git a/spec_builder_helper.go b/spec_builder_helper.go index 3d38572..5b3ee4f 100644 --- a/spec_builder_helper.go +++ b/spec_builder_helper.go @@ -4,7 +4,6 @@ package api import ( "slices" - "strings" ) // OpenAPISpecBuilder returns a SpecBuilder populated from the engine's current @@ -18,6 +17,7 @@ func (e *Engine) OpenAPISpecBuilder() *SpecBuilder { return &SpecBuilder{} } + transport := e.TransportConfig() builder := &SpecBuilder{ Title: e.swaggerTitle, Description: e.swaggerDesc, @@ -33,22 +33,13 @@ func (e *Engine) OpenAPISpecBuilder() *SpecBuilder { ExternalDocsURL: e.swaggerExternalDocsURL, } - if e.swaggerEnabled || strings.TrimSpace(e.swaggerPath) != "" { - builder.SwaggerPath = resolveSwaggerPath(e.swaggerPath) - } - - if e.graphql != nil { - builder.GraphQLPath = e.graphql.path - builder.GraphQLPlayground = e.graphql.playground - } - if e.wsHandler != nil || strings.TrimSpace(e.wsPath) != "" { - builder.WSPath = resolveWSPath(e.wsPath) - } - if e.sseBroker != nil || strings.TrimSpace(e.ssePath) != "" { - builder.SSEPath = resolveSSEPath(e.ssePath) - } - builder.PprofEnabled = e.pprofEnabled - builder.ExpvarEnabled = e.expvarEnabled + builder.SwaggerPath = transport.SwaggerPath + builder.GraphQLPath = transport.GraphQLPath + builder.GraphQLPlayground = transport.GraphQLPlayground + builder.WSPath = transport.WSPath + builder.SSEPath = transport.SSEPath + builder.PprofEnabled = transport.PprofEnabled + builder.ExpvarEnabled = transport.ExpvarEnabled return builder } diff --git a/spec_builder_helper_test.go b/spec_builder_helper_test.go index 7dac236..a2990be 100644 --- a/spec_builder_helper_test.go +++ b/spec_builder_helper_test.go @@ -149,6 +149,49 @@ func TestEngine_Good_OpenAPISpecBuilderCarriesEngineMetadata(t *testing.T) { } } +func TestEngine_Good_TransportConfigCarriesEngineMetadata(t *testing.T) { + gin.SetMode(gin.TestMode) + + broker := api.NewSSEBroker() + e, err := api.New( + api.WithSwagger("Engine API", "Engine metadata", "2.0.0"), + api.WithSwaggerPath("/docs"), + api.WithWSPath("/socket"), + api.WithWSHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})), + api.WithGraphQL(newTestSchema(), api.WithPlayground(), api.WithGraphQLPath("/gql")), + api.WithSSE(broker), + api.WithSSEPath("/events"), + api.WithPprof(), + api.WithExpvar(), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + cfg := e.TransportConfig() + if cfg.SwaggerPath != "/docs" { + t.Fatalf("expected swagger path /docs, got %q", cfg.SwaggerPath) + } + if cfg.GraphQLPath != "/gql" { + t.Fatalf("expected graphql path /gql, got %q", cfg.GraphQLPath) + } + if !cfg.GraphQLPlayground { + t.Fatal("expected GraphQL playground to be enabled") + } + if cfg.WSPath != "/socket" { + t.Fatalf("expected ws path /socket, got %q", cfg.WSPath) + } + if cfg.SSEPath != "/events" { + t.Fatalf("expected sse path /events, got %q", cfg.SSEPath) + } + if !cfg.PprofEnabled { + t.Fatal("expected pprof to be enabled") + } + if !cfg.ExpvarEnabled { + t.Fatal("expected expvar to be enabled") + } +} + func TestEngine_Good_OpenAPISpecBuilderExportsDefaultSwaggerPath(t *testing.T) { gin.SetMode(gin.TestMode) diff --git a/transport.go b/transport.go new file mode 100644 index 0000000..43b6753 --- /dev/null +++ b/transport.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package api + +import "strings" + +// TransportConfig captures the configured transport endpoints and flags for an Engine. +// +// It is intentionally small and serialisable so callers can inspect the active HTTP +// surface without rebuilding an OpenAPI document. +type TransportConfig struct { + SwaggerPath string + GraphQLPath string + GraphQLPlayground bool + WSPath string + SSEPath string + PprofEnabled bool + ExpvarEnabled bool +} + +// TransportConfig returns the currently configured transport metadata for the engine. +// +// The result snapshots the Engine state at call time and normalises any configured +// URL paths using the same rules as the runtime handlers. +func (e *Engine) TransportConfig() TransportConfig { + if e == nil { + return TransportConfig{} + } + + cfg := TransportConfig{ + GraphQLPlayground: e.graphql != nil && e.graphql.playground, + PprofEnabled: e.pprofEnabled, + ExpvarEnabled: e.expvarEnabled, + } + + if e.swaggerEnabled || strings.TrimSpace(e.swaggerPath) != "" { + cfg.SwaggerPath = resolveSwaggerPath(e.swaggerPath) + } + if e.graphql != nil { + cfg.GraphQLPath = e.graphql.path + } + if e.wsHandler != nil || strings.TrimSpace(e.wsPath) != "" { + cfg.WSPath = resolveWSPath(e.wsPath) + } + if e.sseBroker != nil || strings.TrimSpace(e.ssePath) != "" { + cfg.SSEPath = resolveSSEPath(e.ssePath) + } + + return cfg +}