Skip to content

Commit 3690b66

Browse files
authored
Rework router (#210)
1 parent 3794d14 commit 3690b66

23 files changed

+745
-369
lines changed

README.md

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ Here's some projects that depend on _kin-openapi_:
3737
* Support for OpenAPI 3 files, including serialization, deserialization, and validation.
3838
* _openapi3filter_ ([godoc](https://godoc.org/github.com/getkin/kin-openapi/openapi3filter))
3939
* Validates HTTP requests and responses
40+
* Provides a [gorilla/mux](https://github.com/gorilla/mux) router for OpenAPI operations
4041
* _openapi3gen_ ([godoc](https://godoc.org/github.com/getkin/kin-openapi/openapi3gen))
4142
* Generates `*openapi3.Schema` values for Go types.
42-
* _pathpattern_ ([godoc](https://godoc.org/github.com/getkin/kin-openapi/pathpattern))
43-
* Matches strings with OpenAPI path patterns ("/path/{parameter}")
4443

4544
# Some recipes
4645
## Loading OpenAPI document
@@ -51,19 +50,12 @@ swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("swagger.json")
5150

5251
## Getting OpenAPI operation that matches request
5352
```go
54-
func GetOperation(httpRequest *http.Request) (*openapi3.Operation, error) {
55-
// Load Swagger file
56-
router := openapi3filter.NewRouter().WithSwaggerFromFile("swagger.json")
57-
58-
// Find route
59-
route, _, err := router.FindRoute("GET", req.URL)
60-
if err != nil {
61-
return nil, err
62-
}
63-
64-
// Get OpenAPI 3 operation
65-
return route.Operation
66-
}
53+
loader := openapi3.NewSwaggerLoader()
54+
spec, _ := loader.LoadSwaggerFromData([]byte(`...`))
55+
_ := spec.Validate(loader.Context)
56+
router, _ := openapi3filter.NewRouter(spec)
57+
route, pathParams, _ := router.FindRoute(httpRequest)
58+
// Do something with route.Operation
6759
```
6860

6961
## Validating HTTP requests/responses
@@ -81,12 +73,15 @@ import (
8173
)
8274

8375
func main() {
84-
router := openapi3filter.NewRouter().WithSwaggerFromFile("swagger.json")
8576
ctx := context.Background()
77+
loader := &openapi3.SwaggerLoader{Context: ctx}
78+
spec, _ := loader.LoadSwaggerFromFile("openapi3_spec.json")
79+
_ := spec.Validate(ctx)
80+
router, _ := openapi3filter.NewRouter(spec)
8681
httpReq, _ := http.NewRequest(http.MethodGet, "/items", nil)
8782

8883
// Find route
89-
route, pathParams, _ := router.FindRoute(httpReq.Method, httpReq.URL)
84+
route, pathParams, _ := router.FindRoute(httpReq)
9085

9186
// Validate request
9287
requestValidationInput := &openapi3filter.RequestValidationInput{

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.16
55
require (
66
github.com/ghodss/yaml v1.0.0
77
github.com/go-openapi/jsonpointer v0.19.5
8+
github.com/gorilla/mux v1.8.0
89
github.com/stretchr/testify v1.5.1
910
gopkg.in/yaml.v2 v2.3.0 // indirect
1011
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe
77
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
88
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
99
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
10+
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
11+
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
1012
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
1113
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
1214
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

openapi3/server.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package openapi3
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"math"
78
"net/url"
89
"strings"
@@ -128,7 +129,17 @@ func (server *Server) Validate(c context.Context) (err error) {
128129
if server.URL == "" {
129130
return errors.New("value of url must be a non-empty string")
130131
}
131-
for _, v := range server.Variables {
132+
opening, closing := strings.Count(server.URL, "{"), strings.Count(server.URL, "}")
133+
if opening != closing {
134+
return errors.New("server URL has mismatched { and }")
135+
}
136+
if opening != len(server.Variables) {
137+
return errors.New("server has undeclared variables")
138+
}
139+
for name, v := range server.Variables {
140+
if !strings.Contains(server.URL, fmt.Sprintf("{%s}", name)) {
141+
return errors.New("server has undeclared variables")
142+
}
132143
if err = v.Validate(c); err != nil {
133144
return
134145
}
@@ -154,7 +165,7 @@ func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error {
154165

155166
func (serverVariable *ServerVariable) Validate(c context.Context) error {
156167
switch serverVariable.Default.(type) {
157-
case float64, string:
168+
case float64, string, nil:
158169
default:
159170
return errors.New("value of default must be either a number or a string")
160171
}

openapi3/swagger_loader_test.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package openapi3
22

33
import (
4+
"errors"
45
"fmt"
56
"net"
67
"net/http"
78
"net/http/httptest"
89
"net/url"
10+
"strings"
911
"testing"
1012

1113
"github.com/stretchr/testify/require"
@@ -82,8 +84,8 @@ func TestResolveSchemaRef(t *testing.T) {
8284
doc, err := loader.LoadSwaggerFromData(source)
8385
require.NoError(t, err)
8486
err = doc.Validate(loader.Context)
85-
8687
require.NoError(t, err)
88+
8789
refAVisited := doc.Components.Schemas["A"].Value.AllOf[0]
8890
require.Equal(t, "#/components/schemas/B", refAVisited.Ref)
8991
require.NotNil(t, refAVisited.Value)
@@ -267,7 +269,6 @@ func TestLoadFileWithExternalSchemaRef(t *testing.T) {
267269
loader.IsExternalRefsAllowed = true
268270
swagger, err := loader.LoadSwaggerFromFile("testdata/testref.openapi.json")
269271
require.NoError(t, err)
270-
271272
require.NotNil(t, swagger.Components.Schemas["AnotherTestSchema"].Value.Type)
272273
}
273274

@@ -497,3 +498,33 @@ paths:
497498
err = doc.Validate(loader.Context)
498499
require.NoError(t, err)
499500
}
501+
502+
func TestServersVariables(t *testing.T) {
503+
const spec = `
504+
openapi: 3.0.1
505+
info:
506+
title: My API
507+
version: 1.0.0
508+
paths: {}
509+
servers:
510+
- @@@
511+
`
512+
for value, expected := range map[string]error{
513+
`{url: /}`: nil,
514+
`{url: "http://{x}.{y}.example.com"}`: errors.New("invalid servers: server has undeclared variables"),
515+
`{url: "http://{x}.y}.example.com"}`: errors.New("invalid servers: server URL has mismatched { and }"),
516+
`{url: "http://{x.example.com"}`: errors.New("invalid servers: server URL has mismatched { and }"),
517+
`{url: "http://{x}.example.com", variables: {x: {default: "www"}}}`: nil,
518+
`{url: "http://{x}.example.com", variables: {x: {enum: ["www"]}}}`: nil,
519+
`{url: "http://www.example.com", variables: {x: {enum: ["www"]}}}`: errors.New("invalid servers: server has undeclared variables"),
520+
`{url: "http://{y}.example.com", variables: {x: {enum: ["www"]}}}`: errors.New("invalid servers: server has undeclared variables"),
521+
} {
522+
t.Run(value, func(t *testing.T) {
523+
loader := NewSwaggerLoader()
524+
doc, err := loader.LoadSwaggerFromData([]byte(strings.Replace(spec, "@@@", value, 1)))
525+
require.NoError(t, err)
526+
err = doc.Validate(loader.Context)
527+
require.Equal(t, expected, err)
528+
})
529+
}
530+
}

openapi3filter/errors.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,6 @@ import (
66
"github.com/getkin/kin-openapi/openapi3"
77
)
88

9-
type RouteError struct {
10-
Route Route
11-
Reason string
12-
}
13-
14-
func (err *RouteError) Error() string {
15-
return err.Reason
16-
}
17-
189
var _ error = &RequestError{}
1910

2011
// RequestError is returned by ValidateRequest when request does not match OpenAPI spec

openapi3filter/req_resp_decoder_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"testing"
1717

1818
"github.com/getkin/kin-openapi/openapi3"
19+
legacyrouter "github.com/getkin/kin-openapi/routers/legacy"
1920
"github.com/stretchr/testify/require"
2021
)
2122

@@ -922,10 +923,10 @@ func TestDecodeParameter(t *testing.T) {
922923
spec.AddOperation(path, http.MethodGet, op)
923924
err = spec.Validate(context.Background())
924925
require.NoError(t, err)
925-
router := NewRouter()
926-
require.NoError(t, router.AddSwagger(spec))
926+
router, err := legacyrouter.NewRouter(spec)
927+
require.NoError(t, err)
927928

928-
route, pathParams, err := router.FindRoute(req.Method, req.URL)
929+
route, pathParams, err := router.FindRoute(req)
929930
require.NoError(t, err)
930931

931932
input := &RequestValidationInput{Request: req, PathParams: pathParams, Route: route}

0 commit comments

Comments
 (0)