From 1dd9dd449ab2a3e3c82d7005bfee4bec3786cf52 Mon Sep 17 00:00:00 2001 From: dshyjtdes8888 <128817774+dshyjtdes8888@users.noreply.github.com> Date: Sat, 18 May 2024 20:05:04 +0800 Subject: [PATCH] refactor: replace the go model from custom to the official (#439) --- cmd/run.go | 6 +- go.mod | 6 ++ go.sum | 16 +++++- go.work.sum | 2 +- pkg/apispec/swagger.go | 111 +++++++++++++++++++----------------- pkg/apispec/swagger_test.go | 15 +++-- pkg/runner/http.go | 14 +++-- 7 files changed, 101 insertions(+), 69 deletions(-) diff --git a/cmd/run.go b/cmd/run.go index ad1343b..8bcce6a 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -176,10 +176,10 @@ func (o *runOption) preRunE(cmd *cobra.Command, args []string) (err error) { } if err == nil { - var swaggerAPI apispec.APIConverage + var swaggerAPI apispec.SwaggerAPI if o.swaggerURL != "" { - if swaggerAPI, err = apispec.ParseURLToSwagger(o.swaggerURL); err == nil { - o.reportWriter.WithAPIConverage(swaggerAPI) + if swaggerAPI.Swagger, err = apispec.ParseURLToSwagger(o.swaggerURL); err == nil { + o.reportWriter.WithAPIConverage(&swaggerAPI) } } } diff --git a/go.mod b/go.mod index 53055af..71b48fe 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v1.4.1 github.com/go-logr/zapr v1.3.0 + github.com/go-openapi/spec v0.21.0 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 github.com/h2non/gock v1.2.0 @@ -45,6 +46,9 @@ require ( github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect github.com/cucumber/messages-go/v16 v16.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect @@ -56,6 +60,8 @@ require ( github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/phpdave11/gofpdi v1.0.14-0.20211212211723-1f10f9844311 // indirect diff --git a/go.sum b/go.sum index b072e3d..0eace29 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,14 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -93,6 +101,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77 github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -109,6 +119,8 @@ github.com/linuxsuren/go-service v0.0.0-20231225060426-efabcd3a5161 h1:dSL/ah6za github.com/linuxsuren/go-service v0.0.0-20231225060426-efabcd3a5161/go.mod h1:QX22v61PxpOfJa4Xug8qzGTbPjclDZFx2j1PlGLknJw= github.com/linuxsuren/unstructured v0.0.1 h1:ilUA8MUYbR6l9ebo/YPV2bKqlf62bzQursDSE+j00iU= github.com/linuxsuren/unstructured v0.0.1/go.mod h1:KH6aTj+FegzGBzc1vS6mzZx3/duhTUTEVyW5sO7p4as= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -132,8 +144,8 @@ github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57 github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v3 v3.1.0 h1:levPcBfnazlA1CyCMC3asL/QLZkq9pa8tQZOH513zQw= github.com/santhosh-tekuri/jsonschema/v3 v3.1.0/go.mod h1:8kzK2TC0k0YjOForaAHdNEa7ik0fokNa2k30BKJ/W7Y= diff --git a/go.work.sum b/go.work.sum index 22053cd..7e61232 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1384,7 +1384,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -1611,6 +1610,7 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4 h1:BN/Nyn2nWMoqGRA7G7paDNDqTXE30mXGqzzybrfo05w= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= diff --git a/pkg/apispec/swagger.go b/pkg/apispec/swagger.go index a859594..f066e7d 100644 --- a/pkg/apispec/swagger.go +++ b/pkg/apispec/swagger.go @@ -17,61 +17,72 @@ limitations under the License. package apispec import ( - "encoding/json" + "github.com/go-openapi/spec" "io" "net/http" "regexp" "strings" ) -type Swagger struct { - Swagger string `json:"swagger"` - // Paths includes all the API requests. - // The keys is the HTTP request method which as lower-case, for example: get, post. - Paths map[string]map[string]SwaggerAPI `json:"paths"` - Info SwaggerInfo `json:"info"` -} - -type SwaggerAPI struct { - OperationId string `json:"operationId"` - Parameters []Parameter `json:"parameters"` - Summary string `json:"summary"` -} - -type Parameter struct { - Name string `json:"name"` - // In represents the parameter type, supported values: query, path - In string `json:"in"` - Required bool `json:"required"` - Schema Schema `json:"schema"` -} - -type Schema struct { - Type string `json:"type"` - Format string `json:"format"` -} - -type SwaggerInfo struct { - Description string `json:"description"` - Title string `json:"title"` - Version string `json:"version"` -} - type APIConverage interface { HaveAPI(path, method string) (exist bool) APICount() (count int) } +type SwaggerAPI struct { + Swagger *spec.Swagger + ApiMap map[string][]string +} + +func NewSwaggerAPI(swagger *spec.Swagger) *SwaggerAPI { + return &SwaggerAPI{ + Swagger: swagger, + ApiMap: buildAPIMap(swagger), + } +} + +func buildAPIMap(swagger *spec.Swagger) map[string][]string { + apiMap := make(map[string][]string) + for path, pathItem := range swagger.Paths.Paths { + var methods []string + if pathItem.Get != nil { + methods = append(methods, "get") + } + if pathItem.Put != nil { + methods = append(methods, "put") + } + if pathItem.Post != nil { + methods = append(methods, "post") + } + if pathItem.Delete != nil { + methods = append(methods, "delete") + } + if pathItem.Options != nil { + methods = append(methods, "options") + } + if pathItem.Head != nil { + methods = append(methods, "head") + } + if pathItem.Patch != nil { + methods = append(methods, "patch") + } + apiMap[path] = methods + } + return apiMap +} + // HaveAPI check if the swagger has the API. // If the path is /api/v1/names/linuxsuren, then will match /api/v1/names/{name} -func (s *Swagger) HaveAPI(path, method string) (exist bool) { +func (s *SwaggerAPI) HaveAPI(path, method string) (exist bool) { method = strings.ToLower(method) - for item := range s.Paths { - if matchAPI(path, item) { - for m := range s.Paths[item] { - if strings.ToLower(m) == method { - exist = true - return + for p := range s.ApiMap { + if matchAPI(path, p) { + if methods, ok := s.ApiMap[p]; ok { + for _, m := range methods { + if m == method { + exist = true + return + } } } } @@ -98,22 +109,20 @@ func swaggerAPIConvert(text string) (result string) { } // APICount return the count of APIs -func (s *Swagger) APICount() (count int) { - for path := range s.Paths { - for range s.Paths[path] { - count++ - } +func (s *SwaggerAPI) APICount() (count int) { + for _, methods := range s.ApiMap { + count += len(methods) } return } -func ParseToSwagger(data []byte) (swagger *Swagger, err error) { - swagger = &Swagger{} - err = json.Unmarshal(data, swagger) +func ParseToSwagger(data []byte) (swagger *spec.Swagger, err error) { + swagger = &spec.Swagger{} + err = swagger.UnmarshalJSON(data) return } -func ParseURLToSwagger(swaggerURL string) (swagger *Swagger, err error) { +func ParseURLToSwagger(swaggerURL string) (swagger *spec.Swagger, err error) { var resp *http.Response if resp, err = http.Get(swaggerURL); err == nil && resp != nil && resp.StatusCode == http.StatusOK { swagger, err = ParseStreamToSwagger(resp.Body) @@ -121,7 +130,7 @@ func ParseURLToSwagger(swaggerURL string) (swagger *Swagger, err error) { return } -func ParseStreamToSwagger(stream io.Reader) (swagger *Swagger, err error) { +func ParseStreamToSwagger(stream io.Reader) (swagger *spec.Swagger, err error) { var data []byte if data, err = io.ReadAll(stream); err == nil { swagger, err = ParseToSwagger(data) diff --git a/pkg/apispec/swagger_test.go b/pkg/apispec/swagger_test.go index b31ef17..44cc852 100644 --- a/pkg/apispec/swagger_test.go +++ b/pkg/apispec/swagger_test.go @@ -17,6 +17,7 @@ limitations under the License. package apispec_test import ( + "github.com/go-openapi/spec" "net/http" "testing" @@ -31,18 +32,18 @@ func TestParseURLToSwagger(t *testing.T) { tests := []struct { name string swaggerURL string - verify func(t *testing.T, swagger *apispec.Swagger, err error) + verify func(t *testing.T, swagger *spec.Swagger, err error) }{{ name: "normal", swaggerURL: urlFoo, - verify: func(t *testing.T, swagger *apispec.Swagger, err error) { + verify: func(t *testing.T, swagger *spec.Swagger, err error) { assert.NoError(t, err) assert.Equal(t, "2.0", swagger.Swagger) - assert.Equal(t, apispec.SwaggerInfo{ + assert.Equal(t, spec.InfoProps{ Description: "sample", Title: "sample", Version: "1.0.0", - }, swagger.Info) + }, swagger.Info.InfoProps) }, }} for _, tt := range tests { @@ -93,8 +94,9 @@ func TestHaveAPI(t *testing.T) { defer gock.Off() swagger, err := apispec.ParseURLToSwagger(tt.swaggerURL) + swaggerAPI := apispec.NewSwaggerAPI(swagger) assert.NoError(t, err) - exist := swagger.HaveAPI(tt.path, tt.method) + exist := swaggerAPI.HaveAPI(tt.path, tt.method) assert.Equal(t, tt.expectExist, exist) }) } @@ -116,8 +118,9 @@ func TestAPICount(t *testing.T) { defer gock.Off() swagger, err := apispec.ParseURLToSwagger(tt.swaggerURL) + swaggerAPI := apispec.NewSwaggerAPI(swagger) assert.NoError(t, err) - count := swagger.APICount() + count := swaggerAPI.APICount() assert.Equal(t, tt.expectCount, count) }) } diff --git a/pkg/runner/http.go b/pkg/runner/http.go index 598d6cb..e5458d8 100644 --- a/pkg/runner/http.go +++ b/pkg/runner/http.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "github.com/go-openapi/spec" "io" "net/http" "strings" @@ -230,13 +231,14 @@ func (r *simpleTestCaseRunner) GetSuggestedAPIs(suite *testing.TestSuite, api st return } - var swaggerAPI *apispec.Swagger - if swaggerAPI, err = apispec.ParseURLToSwagger(suite.Spec.URL); err == nil && swaggerAPI != nil { + var swagger *spec.Swagger + if swagger, err = apispec.ParseURLToSwagger(suite.Spec.URL); err == nil && swagger != nil { result = []*testing.TestCase{} - for api, item := range swaggerAPI.Paths { - for method, oper := range item { + swaggerAPI := apispec.NewSwaggerAPI(swagger) + for api, methods := range swaggerAPI.ApiMap { + for _, method := range methods { testcase := &testing.TestCase{ - Name: oper.OperationId, + Name: swagger.ID, Request: testing.Request{ API: api, Method: strings.ToUpper(method), @@ -244,7 +246,7 @@ func (r *simpleTestCaseRunner) GetSuggestedAPIs(suite *testing.TestSuite, api st }, } - for _, param := range oper.Parameters { + for _, param := range swagger.Parameters { switch param.In { case "query": // TODO should have a better way to provide the initial value