Skip to content

Commit d914826

Browse files
authored
Introduce abstraction to describe operation and reflector (#65)
1 parent c18f7f7 commit d914826

20 files changed

+2352
-453
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ require (
66
github.com/bool64/dev v0.2.29
77
github.com/stretchr/testify v1.8.2
88
github.com/swaggest/assertjson v1.9.0
9-
github.com/swaggest/jsonschema-go v0.3.52
10-
github.com/swaggest/refl v1.1.0
9+
github.com/swaggest/jsonschema-go v0.3.54
10+
github.com/swaggest/refl v1.2.0
1111
gopkg.in/yaml.v2 v2.4.0
1212
)
1313

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ
3333
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
3434
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
3535
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
36-
github.com/swaggest/jsonschema-go v0.3.52 h1:cFk0Ma34MuZvA+JJ8S5WuQMedwxzUa+9+qrGwtoE39U=
37-
github.com/swaggest/jsonschema-go v0.3.52/go.mod h1:sRly7iaIIvbheAqsyvnKU3H7ogWbF0wtUBXyGpRXNm4=
38-
github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc=
39-
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
36+
github.com/swaggest/jsonschema-go v0.3.54 h1:kRwXd7tqrub8vmtAVV5IElGTE1hiEuo/n9gHrsX7ySY=
37+
github.com/swaggest/jsonschema-go v0.3.54/go.mod h1:iQdEa2VW62As5W+884vA13TC2pEwnKjlQGaQe2pj+fc=
38+
github.com/swaggest/refl v1.2.0 h1:Qqqhfwi7REXF6/4cwJmj3gQMzl0Q0cYquxTYdD0kvi0=
39+
github.com/swaggest/refl v1.2.0/go.mod h1:CkC6g7h1PW33KprTuYRSw8UUOslRUt4lF3oe7tTIgNU=
4040
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
4141
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
4242
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=

internal/operation_context.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Package internal keeps reusable internal code.
2+
package internal
3+
4+
import "github.com/swaggest/openapi-go"
5+
6+
// NewOperationContext creates OperationContext.
7+
func NewOperationContext(method, pathPattern string) *OperationContext {
8+
return &OperationContext{
9+
method: method,
10+
pathPattern: pathPattern,
11+
}
12+
}
13+
14+
// OperationContext implements openapi.OperationContext.
15+
type OperationContext struct {
16+
method string
17+
pathPattern string
18+
req []openapi.ContentUnit
19+
resp []openapi.ContentUnit
20+
21+
isProcessingResponse bool
22+
processingIn openapi.In
23+
}
24+
25+
// Method returns HTTP method of an operation.
26+
func (o *OperationContext) Method() string {
27+
return o.method
28+
}
29+
30+
// PathPattern returns operation HTTP URL path pattern.
31+
func (o *OperationContext) PathPattern() string {
32+
return o.pathPattern
33+
}
34+
35+
// Request returns list of operation request content schemas.
36+
func (o *OperationContext) Request() []openapi.ContentUnit {
37+
return o.req
38+
}
39+
40+
// Response returns list of operation response content schemas.
41+
func (o *OperationContext) Response() []openapi.ContentUnit {
42+
return o.resp
43+
}
44+
45+
// SetIsProcessingResponse sets current processing state.
46+
func (o *OperationContext) SetIsProcessingResponse(is bool) {
47+
o.isProcessingResponse = is
48+
}
49+
50+
// IsProcessingResponse indicates if response is being processed.
51+
func (o *OperationContext) IsProcessingResponse() bool {
52+
return o.isProcessingResponse
53+
}
54+
55+
// SetProcessingIn sets current content location being processed.
56+
func (o *OperationContext) SetProcessingIn(in openapi.In) {
57+
o.processingIn = in
58+
}
59+
60+
// ProcessingIn return which content location is being processed now.
61+
func (o *OperationContext) ProcessingIn() openapi.In {
62+
return o.processingIn
63+
}
64+
65+
// SetMethod sets HTTP method of an operation.
66+
func (o *OperationContext) SetMethod(method string) {
67+
o.method = method
68+
}
69+
70+
// SetPathPattern sets URL path pattern of an operation.
71+
func (o *OperationContext) SetPathPattern(pattern string) {
72+
o.pathPattern = pattern
73+
}
74+
75+
// AddReqStructure adds request content schema.
76+
func (o *OperationContext) AddReqStructure(s interface{}, options ...openapi.ContentOption) {
77+
c := openapi.ContentUnit{}
78+
c.Structure = s
79+
80+
for _, o := range options {
81+
o(&c)
82+
}
83+
84+
o.req = append(o.req, c)
85+
}
86+
87+
// AddRespStructure adds response content schema.
88+
func (o *OperationContext) AddRespStructure(s interface{}, options ...openapi.ContentOption) {
89+
c := openapi.ContentUnit{}
90+
c.Structure = s
91+
92+
for _, o := range options {
93+
o(&c)
94+
}
95+
96+
o.resp = append(o.resp, c)
97+
}

marker.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package openapi
2+
3+
// RequestBodyEnforcer enables request body for GET and HEAD methods.
4+
//
5+
// Should be implemented on input structure, function body can be empty.
6+
// Forcing request body is not recommended and should only be used for backwards compatibility.
7+
type RequestBodyEnforcer interface {
8+
ForceRequestBody()
9+
}
10+
11+
// RequestJSONBodyEnforcer enables JSON request body for structures with `formData` tags.
12+
//
13+
// Should be implemented on input structure, function body can be empty.
14+
type RequestJSONBodyEnforcer interface {
15+
ForceJSONRequestBody()
16+
}

openapi3/_testdata/openapi.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,6 @@
8181
},
8282
"409":{
8383
"description":"Conflict",
84-
"headers":{
85-
"X-Header-Field":{
86-
"style":"simple","description":"Sample header response.",
87-
"schema":{"type":"string","description":"Sample header response."}
88-
}
89-
},
9084
"content":{
9185
"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Openapi3TestResp"}}},
9286
"text/html":{"schema":{"type":"string"}}

openapi3/_testdata/swgui/go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module swgui
2+
3+
go 1.19
4+
5+
require github.com/swaggest/swgui v1.6.4
6+
7+
require (
8+
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 // indirect
9+
golang.org/x/net v0.8.0 // indirect
10+
golang.org/x/text v0.8.0 // indirect
11+
)

openapi3/_testdata/swgui/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/bool64/dev v0.2.29 h1:x+syGyh+0eWtOzQ1ItvLzOGIWyNWnyjXpHIcpF2HvL4=
2+
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 h1:mj/nMDAwTBiaCqMEs4cYCqF7pO6Np7vhy1D1wcQGz+E=
3+
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
4+
github.com/swaggest/swgui v1.6.4 h1:9G7HTUMOAu/Y9YF2wDvoq9GXFlV4BH5y8BSOl4MWfl8=
5+
github.com/swaggest/swgui v1.6.4/go.mod h1:xsfGb4NtnBspBXKXtlPdVrvoqzCIZ338Aj3tHNikz2Q=
6+
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
7+
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
8+
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
9+
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
10+
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=

openapi3/_testdata/swgui/swgui.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package main
22

33
import (
4-
"io/ioutil"
54
"log"
65
"net/http"
6+
"os"
77

8-
v3 "github.com/swaggest/swgui/v3"
8+
swgui "github.com/swaggest/swgui/v5"
99
)
1010

1111
func main() {
12-
h := v3.NewHandler("Foo", "/openapi.json", "/")
12+
h := swgui.NewHandler("Foo", "/openapi.json", "/")
1313
hh := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
1414
if r.URL.Path == "/openapi.json" {
15-
o, err := ioutil.ReadFile("openapi3/_testdata/openapi.json")
15+
o, err := os.ReadFile("../openapi.json")
1616
if err != nil {
1717
http.Error(rw, err.Error(), 500)
1818
return
@@ -25,5 +25,5 @@ func main() {
2525
h.ServeHTTP(rw, r)
2626
})
2727
log.Println("Starting Swagger UI server at http://localhost:8082/")
28-
http.ListenAndServe(":8082", hh)
28+
_ = http.ListenAndServe("localhost:8082", hh)
2929
}

openapi3/example_misc_test.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,10 @@ func ExampleReflector_options() {
2626
Foo []int `json:"foo"`
2727
}
2828

29-
oc := openapi3.OperationContext{
30-
Operation: &openapi3.Operation{},
31-
Input: new(req),
32-
}
29+
oc, _ := r.NewOperationContext(http.MethodPost, "/foo")
30+
oc.AddReqStructure(new(req))
3331

34-
_ = r.SetupRequest(oc)
35-
_ = r.SpecEns().AddOperation(http.MethodGet, "/foo", *oc.Operation)
32+
_ = r.AddOperation(oc)
3633

3734
j, _ := assertjson.MarshalIndentCompact(r.Spec, "", " ", 120)
3835

@@ -43,7 +40,7 @@ func ExampleReflector_options() {
4340
// "openapi":"3.0.3","info":{"title":"","version":""},
4441
// "paths":{
4542
// "/foo":{
46-
// "get":{
43+
// "post":{
4744
// "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Openapi3TestReq"}}}},
4845
// "responses":{"204":{"description":"No Content"}}
4946
// }

openapi3/example_security_test.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import (
55
"log"
66
"net/http"
77

8+
"github.com/swaggest/openapi-go"
89
"github.com/swaggest/openapi-go/openapi3"
910
)
1011

11-
func ExampleReflector_SetJSONResponse_http_basic_auth() {
12+
func ExampleReflector_AddOperation_http_basic_auth() {
1213
reflector := openapi3.Reflector{}
1314
securityName := "admin"
1415

@@ -22,21 +23,23 @@ func ExampleReflector_SetJSONResponse_http_basic_auth() {
2223
},
2324
)
2425

25-
op := openapi3.Operation{}
26-
_ = reflector.SetJSONResponse(&op, struct {
26+
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
27+
oc.AddRespStructure(struct {
2728
Secret string `json:"secret"`
28-
}{}, http.StatusOK)
29+
}{})
2930

3031
// Add security requirement to operation.
31-
op.Security = append(op.Security, map[string][]string{securityName: {}})
32+
oc.AddSecurity(securityName)
3233

3334
// Describe unauthorized response.
34-
_ = reflector.SetJSONResponse(&op, struct {
35+
oc.AddRespStructure(struct {
3536
Error string `json:"error"`
36-
}{}, http.StatusUnauthorized)
37+
}{}, func(cu *openapi.ContentUnit) {
38+
cu.HTTPStatus = http.StatusUnauthorized
39+
})
3740

3841
// Add operation to schema.
39-
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
42+
_ = reflector.AddOperation(oc)
4043

4144
schema, err := reflector.Spec.MarshalYAML()
4245
if err != nil {
@@ -82,7 +85,7 @@ func ExampleReflector_SetJSONResponse_http_basic_auth() {
8285
// type: http
8386
}
8487

85-
func ExampleReflector_SetJSONResponse_api_key_auth() {
88+
func ExampleReflector_AddOperation_api_key_auth() {
8689
reflector := openapi3.Reflector{}
8790
securityName := "api_key"
8891

@@ -99,21 +102,23 @@ func ExampleReflector_SetJSONResponse_api_key_auth() {
99102
},
100103
)
101104

102-
op := openapi3.Operation{}
103-
_ = reflector.SetJSONResponse(&op, struct {
105+
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
106+
oc.AddRespStructure(struct {
104107
Secret string `json:"secret"`
105-
}{}, http.StatusOK)
108+
}{})
106109

107110
// Add security requirement to operation.
108-
op.Security = append(op.Security, map[string][]string{securityName: {}})
111+
oc.AddSecurity(securityName)
109112

110113
// Describe unauthorized response.
111-
_ = reflector.SetJSONResponse(&op, struct {
114+
oc.AddRespStructure(struct {
112115
Error string `json:"error"`
113-
}{}, http.StatusUnauthorized)
116+
}{}, func(cu *openapi.ContentUnit) {
117+
cu.HTTPStatus = http.StatusUnauthorized
118+
})
114119

115120
// Add operation to schema.
116-
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
121+
_ = reflector.AddOperation(oc)
117122

118123
schema, err := reflector.Spec.MarshalYAML()
119124
if err != nil {
@@ -160,7 +165,7 @@ func ExampleReflector_SetJSONResponse_api_key_auth() {
160165
// type: apiKey
161166
}
162167

163-
func ExampleReflector_SetJSONResponse_http_bearer_token_auth() {
168+
func ExampleReflector_AddOperation_http_bearer_token_auth() {
164169
reflector := openapi3.Reflector{}
165170
securityName := "bearer_token"
166171

@@ -177,21 +182,23 @@ func ExampleReflector_SetJSONResponse_http_bearer_token_auth() {
177182
},
178183
)
179184

180-
op := openapi3.Operation{}
181-
_ = reflector.SetJSONResponse(&op, struct {
185+
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
186+
oc.AddRespStructure(struct {
182187
Secret string `json:"secret"`
183-
}{}, http.StatusOK)
188+
}{})
184189

185190
// Add security requirement to operation.
186-
op.Security = append(op.Security, map[string][]string{securityName: {}})
191+
oc.AddSecurity(securityName)
187192

188193
// Describe unauthorized response.
189-
_ = reflector.SetJSONResponse(&op, struct {
194+
oc.AddRespStructure(struct {
190195
Error string `json:"error"`
191-
}{}, http.StatusUnauthorized)
196+
}{}, func(cu *openapi.ContentUnit) {
197+
cu.HTTPStatus = http.StatusUnauthorized
198+
})
192199

193200
// Add operation to schema.
194-
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
201+
_ = reflector.AddOperation(oc)
195202

196203
schema, err := reflector.Spec.MarshalYAML()
197204
if err != nil {

0 commit comments

Comments
 (0)