Skip to content

Commit

Permalink
Introduce abstraction to describe operation and reflector (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop authored Jul 28, 2023
1 parent c18f7f7 commit d914826
Show file tree
Hide file tree
Showing 20 changed files with 2,352 additions and 453 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ require (
github.com/bool64/dev v0.2.29
github.com/stretchr/testify v1.8.2
github.com/swaggest/assertjson v1.9.0
github.com/swaggest/jsonschema-go v0.3.52
github.com/swaggest/refl v1.1.0
github.com/swaggest/jsonschema-go v0.3.54
github.com/swaggest/refl v1.2.0
gopkg.in/yaml.v2 v2.4.0
)

Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
github.com/swaggest/jsonschema-go v0.3.52 h1:cFk0Ma34MuZvA+JJ8S5WuQMedwxzUa+9+qrGwtoE39U=
github.com/swaggest/jsonschema-go v0.3.52/go.mod h1:sRly7iaIIvbheAqsyvnKU3H7ogWbF0wtUBXyGpRXNm4=
github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc=
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
github.com/swaggest/jsonschema-go v0.3.54 h1:kRwXd7tqrub8vmtAVV5IElGTE1hiEuo/n9gHrsX7ySY=
github.com/swaggest/jsonschema-go v0.3.54/go.mod h1:iQdEa2VW62As5W+884vA13TC2pEwnKjlQGaQe2pj+fc=
github.com/swaggest/refl v1.2.0 h1:Qqqhfwi7REXF6/4cwJmj3gQMzl0Q0cYquxTYdD0kvi0=
github.com/swaggest/refl v1.2.0/go.mod h1:CkC6g7h1PW33KprTuYRSw8UUOslRUt4lF3oe7tTIgNU=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
Expand Down
97 changes: 97 additions & 0 deletions internal/operation_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Package internal keeps reusable internal code.
package internal

import "github.com/swaggest/openapi-go"

// NewOperationContext creates OperationContext.
func NewOperationContext(method, pathPattern string) *OperationContext {
return &OperationContext{
method: method,
pathPattern: pathPattern,
}
}

// OperationContext implements openapi.OperationContext.
type OperationContext struct {
method string
pathPattern string
req []openapi.ContentUnit
resp []openapi.ContentUnit

isProcessingResponse bool
processingIn openapi.In
}

// Method returns HTTP method of an operation.
func (o *OperationContext) Method() string {
return o.method
}

// PathPattern returns operation HTTP URL path pattern.
func (o *OperationContext) PathPattern() string {
return o.pathPattern
}

// Request returns list of operation request content schemas.
func (o *OperationContext) Request() []openapi.ContentUnit {
return o.req
}

// Response returns list of operation response content schemas.
func (o *OperationContext) Response() []openapi.ContentUnit {
return o.resp
}

// SetIsProcessingResponse sets current processing state.
func (o *OperationContext) SetIsProcessingResponse(is bool) {
o.isProcessingResponse = is
}

// IsProcessingResponse indicates if response is being processed.
func (o *OperationContext) IsProcessingResponse() bool {
return o.isProcessingResponse
}

// SetProcessingIn sets current content location being processed.
func (o *OperationContext) SetProcessingIn(in openapi.In) {
o.processingIn = in
}

// ProcessingIn return which content location is being processed now.
func (o *OperationContext) ProcessingIn() openapi.In {
return o.processingIn
}

// SetMethod sets HTTP method of an operation.
func (o *OperationContext) SetMethod(method string) {
o.method = method
}

// SetPathPattern sets URL path pattern of an operation.
func (o *OperationContext) SetPathPattern(pattern string) {
o.pathPattern = pattern
}

// AddReqStructure adds request content schema.
func (o *OperationContext) AddReqStructure(s interface{}, options ...openapi.ContentOption) {
c := openapi.ContentUnit{}
c.Structure = s

for _, o := range options {
o(&c)
}

o.req = append(o.req, c)
}

// AddRespStructure adds response content schema.
func (o *OperationContext) AddRespStructure(s interface{}, options ...openapi.ContentOption) {
c := openapi.ContentUnit{}
c.Structure = s

for _, o := range options {
o(&c)
}

o.resp = append(o.resp, c)
}
16 changes: 16 additions & 0 deletions marker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package openapi

// RequestBodyEnforcer enables request body for GET and HEAD methods.
//
// Should be implemented on input structure, function body can be empty.
// Forcing request body is not recommended and should only be used for backwards compatibility.
type RequestBodyEnforcer interface {
ForceRequestBody()
}

// RequestJSONBodyEnforcer enables JSON request body for structures with `formData` tags.
//
// Should be implemented on input structure, function body can be empty.
type RequestJSONBodyEnforcer interface {
ForceJSONRequestBody()
}
6 changes: 0 additions & 6 deletions openapi3/_testdata/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@
},
"409":{
"description":"Conflict",
"headers":{
"X-Header-Field":{
"style":"simple","description":"Sample header response.",
"schema":{"type":"string","description":"Sample header response."}
}
},
"content":{
"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Openapi3TestResp"}}},
"text/html":{"schema":{"type":"string"}}
Expand Down
11 changes: 11 additions & 0 deletions openapi3/_testdata/swgui/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module swgui

go 1.19

require github.com/swaggest/swgui v1.6.4

require (
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect
)
10 changes: 10 additions & 0 deletions openapi3/_testdata/swgui/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/bool64/dev v0.2.29 h1:x+syGyh+0eWtOzQ1ItvLzOGIWyNWnyjXpHIcpF2HvL4=
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 h1:mj/nMDAwTBiaCqMEs4cYCqF7pO6Np7vhy1D1wcQGz+E=
github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/swaggest/swgui v1.6.4 h1:9G7HTUMOAu/Y9YF2wDvoq9GXFlV4BH5y8BSOl4MWfl8=
github.com/swaggest/swgui v1.6.4/go.mod h1:xsfGb4NtnBspBXKXtlPdVrvoqzCIZ338Aj3tHNikz2Q=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
10 changes: 5 additions & 5 deletions openapi3/_testdata/swgui/swgui.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package main

import (
"io/ioutil"
"log"
"net/http"
"os"

v3 "github.com/swaggest/swgui/v3"
swgui "github.com/swaggest/swgui/v5"
)

func main() {
h := v3.NewHandler("Foo", "/openapi.json", "/")
h := swgui.NewHandler("Foo", "/openapi.json", "/")
hh := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/openapi.json" {
o, err := ioutil.ReadFile("openapi3/_testdata/openapi.json")
o, err := os.ReadFile("../openapi.json")
if err != nil {
http.Error(rw, err.Error(), 500)
return
Expand All @@ -25,5 +25,5 @@ func main() {
h.ServeHTTP(rw, r)
})
log.Println("Starting Swagger UI server at http://localhost:8082/")
http.ListenAndServe(":8082", hh)
_ = http.ListenAndServe("localhost:8082", hh)
}
11 changes: 4 additions & 7 deletions openapi3/example_misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,10 @@ func ExampleReflector_options() {
Foo []int `json:"foo"`
}

oc := openapi3.OperationContext{
Operation: &openapi3.Operation{},
Input: new(req),
}
oc, _ := r.NewOperationContext(http.MethodPost, "/foo")
oc.AddReqStructure(new(req))

_ = r.SetupRequest(oc)
_ = r.SpecEns().AddOperation(http.MethodGet, "/foo", *oc.Operation)
_ = r.AddOperation(oc)

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

Expand All @@ -43,7 +40,7 @@ func ExampleReflector_options() {
// "openapi":"3.0.3","info":{"title":"","version":""},
// "paths":{
// "/foo":{
// "get":{
// "post":{
// "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Openapi3TestReq"}}}},
// "responses":{"204":{"description":"No Content"}}
// }
Expand Down
55 changes: 31 additions & 24 deletions openapi3/example_security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"log"
"net/http"

"github.com/swaggest/openapi-go"
"github.com/swaggest/openapi-go/openapi3"
)

func ExampleReflector_SetJSONResponse_http_basic_auth() {
func ExampleReflector_AddOperation_http_basic_auth() {
reflector := openapi3.Reflector{}
securityName := "admin"

Expand All @@ -22,21 +23,23 @@ func ExampleReflector_SetJSONResponse_http_basic_auth() {
},
)

op := openapi3.Operation{}
_ = reflector.SetJSONResponse(&op, struct {
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
oc.AddRespStructure(struct {
Secret string `json:"secret"`
}{}, http.StatusOK)
}{})

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

// Describe unauthorized response.
_ = reflector.SetJSONResponse(&op, struct {
oc.AddRespStructure(struct {
Error string `json:"error"`
}{}, http.StatusUnauthorized)
}{}, func(cu *openapi.ContentUnit) {
cu.HTTPStatus = http.StatusUnauthorized
})

// Add operation to schema.
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
_ = reflector.AddOperation(oc)

schema, err := reflector.Spec.MarshalYAML()
if err != nil {
Expand Down Expand Up @@ -82,7 +85,7 @@ func ExampleReflector_SetJSONResponse_http_basic_auth() {
// type: http
}

func ExampleReflector_SetJSONResponse_api_key_auth() {
func ExampleReflector_AddOperation_api_key_auth() {
reflector := openapi3.Reflector{}
securityName := "api_key"

Expand All @@ -99,21 +102,23 @@ func ExampleReflector_SetJSONResponse_api_key_auth() {
},
)

op := openapi3.Operation{}
_ = reflector.SetJSONResponse(&op, struct {
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
oc.AddRespStructure(struct {
Secret string `json:"secret"`
}{}, http.StatusOK)
}{})

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

// Describe unauthorized response.
_ = reflector.SetJSONResponse(&op, struct {
oc.AddRespStructure(struct {
Error string `json:"error"`
}{}, http.StatusUnauthorized)
}{}, func(cu *openapi.ContentUnit) {
cu.HTTPStatus = http.StatusUnauthorized
})

// Add operation to schema.
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
_ = reflector.AddOperation(oc)

schema, err := reflector.Spec.MarshalYAML()
if err != nil {
Expand Down Expand Up @@ -160,7 +165,7 @@ func ExampleReflector_SetJSONResponse_api_key_auth() {
// type: apiKey
}

func ExampleReflector_SetJSONResponse_http_bearer_token_auth() {
func ExampleReflector_AddOperation_http_bearer_token_auth() {
reflector := openapi3.Reflector{}
securityName := "bearer_token"

Expand All @@ -177,21 +182,23 @@ func ExampleReflector_SetJSONResponse_http_bearer_token_auth() {
},
)

op := openapi3.Operation{}
_ = reflector.SetJSONResponse(&op, struct {
oc, _ := reflector.NewOperationContext(http.MethodGet, "/secure")
oc.AddRespStructure(struct {
Secret string `json:"secret"`
}{}, http.StatusOK)
}{})

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

// Describe unauthorized response.
_ = reflector.SetJSONResponse(&op, struct {
oc.AddRespStructure(struct {
Error string `json:"error"`
}{}, http.StatusUnauthorized)
}{}, func(cu *openapi.ContentUnit) {
cu.HTTPStatus = http.StatusUnauthorized
})

// Add operation to schema.
_ = reflector.SpecEns().AddOperation(http.MethodGet, "/secure", op)
_ = reflector.AddOperation(oc)

schema, err := reflector.Spec.MarshalYAML()
if err != nil {
Expand Down
Loading

0 comments on commit d914826

Please sign in to comment.