diff --git a/client.go b/client.go index 4a901a5..d5ef909 100644 --- a/client.go +++ b/client.go @@ -230,6 +230,7 @@ func (c *OpenAPIClient) loadSpec() error { } } } + c.servers = normaliseServers(c.servers) if c.baseURL == "" { for _, server := range c.servers { diff --git a/client_test.go b/client_test.go index 93558fe..1c8e9e9 100644 --- a/client_test.go +++ b/client_test.go @@ -278,8 +278,9 @@ info: title: Test API version: 1.0.0 servers: + - url: " `+srv.URL+` " - url: / - - url: `+srv.URL+` + - url: " `+srv.URL+` " paths: /hello: get: diff --git a/openapi.go b/openapi.go index 0dc0efc..41ec95f 100644 --- a/openapi.go +++ b/openapi.go @@ -37,17 +37,12 @@ func (sb *SpecBuilder) Build(groups []RouteGroup) ([]byte, error) { }, } - if len(sb.Servers) > 0 { - servers := make([]map[string]any, 0, len(sb.Servers)) - for _, server := range sb.Servers { - if server == "" { - continue - } - servers = append(servers, map[string]any{"url": server}) - } - if len(servers) > 0 { - spec["servers"] = servers + if servers := normaliseServers(sb.Servers); len(servers) > 0 { + out := make([]map[string]any, 0, len(servers)) + for _, server := range servers { + out = append(out, map[string]any{"url": server}) } + spec["servers"] = out } // Add component schemas for the response envelope. diff --git a/openapi_test.go b/openapi_test.go index 7e4972b..8c7e242 100644 --- a/openapi_test.go +++ b/openapi_test.go @@ -779,9 +779,10 @@ func TestSpecBuilder_Good_Servers(t *testing.T) { Title: "Test", Version: "1.0.0", Servers: []string{ - "https://api.example.com", + " https://api.example.com ", "/", "", + "https://api.example.com", }, } @@ -800,7 +801,7 @@ func TestSpecBuilder_Good_Servers(t *testing.T) { t.Fatalf("expected servers array, got %T", spec["servers"]) } if len(servers) != 2 { - t.Fatalf("expected 2 non-empty servers, got %d", len(servers)) + t.Fatalf("expected 2 normalised servers, got %d", len(servers)) } first := servers[0].(map[string]any) diff --git a/options.go b/options.go index 76f6f19..6378e0a 100644 --- a/options.go +++ b/options.go @@ -134,7 +134,7 @@ func WithSwagger(title, description, version string) Option { // server list through both the runtime Swagger UI and exported OpenAPI files. func WithSwaggerServers(servers ...string) Option { return func(e *Engine) { - e.swaggerServers = append([]string(nil), servers...) + e.swaggerServers = normaliseServers(servers) } } diff --git a/servers.go b/servers.go new file mode 100644 index 0000000..5a9f5c1 --- /dev/null +++ b/servers.go @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package api + +import "strings" + +// normaliseServers trims whitespace, removes empty entries, and preserves +// the first occurrence of each server URL. +func normaliseServers(servers []string) []string { + if len(servers) == 0 { + return nil + } + + cleaned := make([]string, 0, len(servers)) + seen := make(map[string]struct{}, len(servers)) + + for _, server := range servers { + server = strings.TrimSpace(server) + if server == "" { + continue + } + if _, ok := seen[server]; ok { + continue + } + seen[server] = struct{}{} + cleaned = append(cleaned, server) + } + + if len(cleaned) == 0 { + return nil + } + + return cleaned +} diff --git a/swagger_test.go b/swagger_test.go index c49a492..689361e 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -263,7 +263,7 @@ func TestSwagger_Good_UsesServerMetadata(t *testing.T) { e, err := api.New( api.WithSwagger("Server API", "Server metadata test", "1.0.0"), - api.WithSwaggerServers("https://api.example.com", "/", ""), + api.WithSwaggerServers(" https://api.example.com ", "/", "", "https://api.example.com"), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -293,7 +293,7 @@ func TestSwagger_Good_UsesServerMetadata(t *testing.T) { t.Fatalf("expected servers array, got %T", doc["servers"]) } if len(servers) != 2 { - t.Fatalf("expected 2 servers, got %d", len(servers)) + t.Fatalf("expected 2 normalised servers, got %d", len(servers)) } first := servers[0].(map[string]any)