diff --git a/routers/gorillamux/router.go b/routers/gorillamux/router.go index 83bbf829e..c2381c6be 100644 --- a/routers/gorillamux/router.go +++ b/routers/gorillamux/router.go @@ -7,6 +7,7 @@ package gorillamux import ( + "fmt" "net/http" "net/url" "sort" @@ -36,7 +37,7 @@ func NewRouter(doc *openapi3.T) (routers.Router, error) { } servers := make([]srv, 0, len(doc.Servers)) for _, server := range doc.Servers { - serverURL := server.URL + serverURL := resolveServerURL(server) var schemes []string var u *url.URL var err error @@ -101,6 +102,19 @@ func NewRouter(doc *openapi3.T) (routers.Router, error) { return r, nil } +// resolveServerURL Resolves variables that may be in the server.URL property +// Each variable that is declared in the OpenAPI document will be replaced +// with its default value. See more info on server variables at +// https://spec.openapis.org/oas/v3.0.3#server-variable-object +func resolveServerURL(server *openapi3.Server) string { + var resolvedValue = server.URL + for key, element := range server.Variables { + // TODO: are OpenAPI Server Variable names case-sensitive? + resolvedValue = strings.Replace(resolvedValue, fmt.Sprintf("{%s}", key), element.Default, -1) + } + return resolvedValue +} + // FindRoute extracts the route and parameters of an http.Request func (r *Router) FindRoute(req *http.Request) (*routers.Route, map[string]string, error) { for i, muxRoute := range r.muxes { diff --git a/routers/gorillamux/router_test.go b/routers/gorillamux/router_test.go index 90f5c3dba..38e0c93a6 100644 --- a/routers/gorillamux/router_test.go +++ b/routers/gorillamux/router_test.go @@ -215,7 +215,12 @@ func TestServerPath(t *testing.T) { _, err = NewRouter(&openapi3.T{Servers: openapi3.Servers{ server, &openapi3.Server{URL: "http://example.com/"}, - &openapi3.Server{URL: "http://example.com/path"}}, + &openapi3.Server{URL: "http://example.com/path"}, + newServerWithVariables( + "{scheme}://localhost", + map[string]string{ + "scheme": "https", + })}, }) require.NoError(t, err) } @@ -242,3 +247,82 @@ func TestRelativeURL(t *testing.T) { require.NoError(t, err) require.Equal(t, "/hello", route.Path) } + +func Test_resolveServerURL(t *testing.T) { + type args struct { + server *openapi3.Server + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Test without any variables at all", + args: args{ + server: newServerWithVariables( + "http://example.com", + nil), + }, + want: "http://example.com", + }, + { + name: "Test entire URL is a single variable", + args: args{ + server: newServerWithVariables( + "{server}", + map[string]string{"server": "/"}), + }, + want: "/", + }, + { + name: "Test with variable scheme", + args: args{ + server: newServerWithVariables( + "{scheme}://localhost", + map[string]string{"scheme": "https"}), + }, + want: "https://localhost", + }, + { + name: "Test variable scheme, port, and root-path", + args: args{ + server: newServerWithVariables( + "{scheme}://localhost:{port}/{root-path}", + map[string]string{"scheme": "https", "port": "8080", "root-path": "api"}), + }, + want: "https://localhost:8080/api", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := resolveServerURL(tt.args.server); got != tt.want { + t.Errorf("resolveServerURL() = %v, want %v", got, tt.want) + } + }) + } +} + +func newServerWithVariables(url string, variables map[string]string) *openapi3.Server { + var serverVariables = map[string]*openapi3.ServerVariable{} + + for key, value := range variables { + serverVariables[key] = newServerVariable(value) + } + + return &openapi3.Server{ + ExtensionProps: openapi3.ExtensionProps{}, + URL: url, + Description: "", + Variables: serverVariables, + } +} + +func newServerVariable(defaultValue string) *openapi3.ServerVariable { + return &openapi3.ServerVariable{ + ExtensionProps: openapi3.ExtensionProps{}, + Enum: nil, + Default: defaultValue, + Description: "", + } +}