From 5b1c65b5917839614f131aab0ec6a66a9552781a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Mon, 30 May 2022 17:26:50 +0800 Subject: [PATCH 01/23] feat: implement DataLoader interface --- .../data_loader/loader/openapi3/export.go | 24 +++ .../data_loader/loader/openapi3/import.go | 155 ++++++++++++++++++ .../data_loader/loader/openapi3/openapi3.go | 41 +++++ 3 files changed, 220 insertions(+) create mode 100644 api/internal/handler/data_loader/loader/openapi3/export.go create mode 100644 api/internal/handler/data_loader/loader/openapi3/import.go create mode 100644 api/internal/handler/data_loader/loader/openapi3/openapi3.go diff --git a/api/internal/handler/data_loader/loader/openapi3/export.go b/api/internal/handler/data_loader/loader/openapi3/export.go new file mode 100644 index 0000000000..9d56dd3a35 --- /dev/null +++ b/api/internal/handler/data_loader/loader/openapi3/export.go @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package openapi3 + +import "github.com/apisix/manager-api/internal/handler/data_loader/loader" + +func (Loader) Export(data loader.DataSets) (interface{}, error) { + //TODO implement me + panic("implement me") +} diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go new file mode 100644 index 0000000000..25469ef0c9 --- /dev/null +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package openapi3 + +import ( + "errors" + "net/url" + "strings" + "time" + + "github.com/getkin/kin-openapi/openapi3" + + "github.com/apisix/manager-api/internal/core/entity" + "github.com/apisix/manager-api/internal/handler/data_loader/loader" + "github.com/apisix/manager-api/internal/utils/consts" +) + +func (o Loader) Import(input interface{}) (*loader.DataSets, error) { + if input == nil { + return nil, errors.New("input is nil") + } + + // load OAS3 document + swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(input.([]byte)) + if err != nil { + return nil, err + } + + // no paths in OAS3 document + if len(swagger.Paths) < 1 { + return nil, consts.ErrImportFile + } + + if o.TaskName == "" { + o.TaskName = "openapi_" + time.Now().Format("20060102150405") + } + + data, err := o.convertToEntities(swagger) + if err != nil { + return nil, err + } + + return data, nil +} + +func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) { + var ( + // temporarily save the parsed data + data = &loader.DataSets{} + // global upstream ID + globalUpstreamID = o.TaskName + "_global" + // global uri prefix + globalPath = "" + ) + + // create upstream when servers field not empty + if len(s.Servers) > 0 { + var upstream entity.Upstream + upstream, globalPath = generateUpstreamByServers(s.Servers, globalUpstreamID) + data.Upstreams = append(data.Upstreams, upstream) + } + + // each one will correspond to a route + for uri, v := range s.Paths { + // replace parameter in uri to wildcard + realUri := regURIVar.ReplaceAllString(uri, "*") + // generate route name + routeID := o.TaskName + "_" + strings.NewReplacer("/", "-", "{", "", "}", "").Replace(strings.TrimPrefix(uri, "/")) + + // decide whether to merge multi method routes based on configuration + if o.MergeMethod { + // create a single route for each path + route := generateBaseRoute(routeID, v.Summary) + route.Uris = []string{globalPath + realUri} + route.UpstreamID = globalUpstreamID + for method := range v.Operations() { + route.Methods = append(route.Methods, strings.ToUpper(method)) + } + data.Routes = append(data.Routes, route) + } else { + // create routes for each method of each path + for method, operation := range v.Operations() { + subRouteID := routeID + "_" + method + route := generateBaseRoute(subRouteID, operation.Summary) + route.Uris = []string{globalPath + realUri} + route.Methods = []string{strings.ToUpper(method)} + route.UpstreamID = globalUpstreamID + data.Routes = append(data.Routes, route) + } + } + } + + return data, nil +} + +// generate upstream from OpenAPI servers field +func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (entity.Upstream, string) { + upstream := entity.Upstream{ + BaseInfo: entity.BaseInfo{ID: upstreamID}, + UpstreamDef: entity.UpstreamDef{ + Name: upstreamID, + Type: "roundrobin", + }, + } + + var ( + nodes = make(map[string]float64) + scheme string + path string + ) + for _, server := range servers { + u, err := url.Parse(server.URL) + if err != nil { + continue + } + + // save the scheme and path of the first valid server + // path may be empty + if scheme == "" { + scheme = u.Scheme + path = u.Path + } + + nodes[u.Host] = 1 + } + + upstream.Scheme = scheme + upstream.Nodes = entity.NodesFormat(nodes) + + return upstream, path +} + +// generate base sample route for customize +func generateBaseRoute(id string, desc string) entity.Route { + return entity.Route{ + BaseInfo: entity.BaseInfo{ID: id}, + Name: id, + Desc: desc, + Plugins: make(map[string]interface{}), + } +} diff --git a/api/internal/handler/data_loader/loader/openapi3/openapi3.go b/api/internal/handler/data_loader/loader/openapi3/openapi3.go new file mode 100644 index 0000000000..3bce52399a --- /dev/null +++ b/api/internal/handler/data_loader/loader/openapi3/openapi3.go @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package openapi3 + +import ( + "regexp" + + "github.com/getkin/kin-openapi/openapi3" +) + +type OpenAPISpecFileType string + +type Loader struct { + // MergeMethod indicates whether to merge routes when multiple HTTP methods are on the same path + MergeMethod bool + // TaskName indicates the name of current import/export task + TaskName string +} + +type PathValue struct { + Method string + Value *openapi3.Operation +} + +var ( + regURIVar = regexp.MustCompile(`{[\w.]*}`) +) From 7b6985dcb5d26bcd5a9d15a4e9f276d34385794e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 31 May 2022 16:56:33 +0800 Subject: [PATCH 02/23] feat: enhance import --- .../data_loader/loader/openapi3/import.go | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 25469ef0c9..681172d962 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -62,7 +62,7 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) // temporarily save the parsed data data = &loader.DataSets{} // global upstream ID - globalUpstreamID = o.TaskName + "_global" + globalUpstreamID = o.TaskName // global uri prefix globalPath = "" ) @@ -83,7 +83,7 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) // decide whether to merge multi method routes based on configuration if o.MergeMethod { - // create a single route for each path + // create a single route for each path, merge all methods route := generateBaseRoute(routeID, v.Summary) route.Uris = []string{globalPath + realUri} route.UpstreamID = globalUpstreamID @@ -107,7 +107,9 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) return data, nil } -// generate upstream from OpenAPI servers field +// Generate APISIX upstream from OpenAPI servers field +// return upstream and uri prefix +// Tips: select only first server in servers field func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (entity.Upstream, string) { upstream := entity.Upstream{ BaseInfo: entity.BaseInfo{ID: upstreamID}, @@ -117,34 +119,21 @@ func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (ent }, } - var ( - nodes = make(map[string]float64) - scheme string - path string - ) - for _, server := range servers { - u, err := url.Parse(server.URL) - if err != nil { - continue - } - - // save the scheme and path of the first valid server - // path may be empty - if scheme == "" { - scheme = u.Scheme - path = u.Path - } - - nodes[u.Host] = 1 + u, err := url.Parse(servers[0].URL) + if err != nil { + // return an empty upstream when parsing url failed + return upstream, "" } - upstream.Scheme = scheme - upstream.Nodes = entity.NodesFormat(nodes) + upstream.Scheme = u.Scheme + upstream.Nodes = map[string]float64{ + u.Host: 1, + } - return upstream, path + return upstream, u.Path } -// generate base sample route for customize +// Generate a base route for customize func generateBaseRoute(id string, desc string) entity.Route { return entity.Route{ BaseInfo: entity.BaseInfo{ID: id}, From 8ac880d3c7f7bd231907138622edd1798bd7c3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 12:33:13 +0800 Subject: [PATCH 03/23] test: add API 101 cases --- .../loader/openapi3/import_test.go | 96 +++++++++++ api/test/testdata/import/Postman-API101.yaml | 158 ++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 api/internal/handler/data_loader/loader/openapi3/import_test.go create mode 100644 api/test/testdata/import/Postman-API101.yaml diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go new file mode 100644 index 0000000000..1175e14883 --- /dev/null +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -0,0 +1,96 @@ +package openapi3 + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/apisix/manager-api/internal/core/entity" +) + +var ( + TestAPI101 = "../../../../../test/testdata/import/Postman-API101.yaml" +) + +// Test API 101 on no MergeMethod mode +func TestParseAPI101NoMerge(t *testing.T) { + fileContent, err := ioutil.ReadFile(TestAPI101) + assert.NoError(t, err) + + l := &Loader{MergeMethod: false, TaskName: "test"} + data, err := l.Import(fileContent) + assert.NoError(t, err) + + assert.Len(t, data.Routes, 5) + assert.Len(t, data.Upstreams, 1) + + // Upstream + assert.Equal(t, "https", data.Upstreams[0].Scheme) + assert.Equal(t, float64(1), data.Upstreams[0].Nodes.(map[string]float64)["api-101.glitch.me"]) + assert.Equal(t, "test", data.Upstreams[0].Name) + assert.Equal(t, "roundrobin", data.Upstreams[0].Type) + + // Route + assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) + for _, route := range data.Routes { + switch route.Name { + case "test_customer_GET": + assert.Contains(t, route.Uris, "/customer") + assert.Contains(t, route.Methods, "GET") + assert.Equal(t, "Get one customer", route.Desc) + assert.Equal(t, entity.Status(0), route.Status) + case "test_customer-customer_id_PUT": + assert.Contains(t, route.Uris, "/customer/*") + assert.Contains(t, route.Methods, "PUT") + assert.Equal(t, "Update customer", route.Desc) + assert.Equal(t, entity.Status(0), route.Status) + case "test_customer-customer_id_DELETE": + assert.Contains(t, route.Uris, "/customer/*") + assert.Contains(t, route.Methods, "DELETE") + assert.Equal(t, "Remove customer", route.Desc) + assert.Equal(t, entity.Status(0), route.Status) + } + } +} + +// Test API 101 on MergeMethod mode +func TestParseAPI101Merge(t *testing.T) { + fileContent, err := ioutil.ReadFile(TestAPI101) + assert.NoError(t, err) + + l := &Loader{MergeMethod: true, TaskName: "test"} + data, err := l.Import(fileContent) + assert.NoError(t, err) + + assert.Len(t, data.Routes, 3) + assert.Len(t, data.Upstreams, 1) + + // Upstream + assert.Equal(t, "https", data.Upstreams[0].Scheme) + assert.Equal(t, float64(1), data.Upstreams[0].Nodes.(map[string]float64)["api-101.glitch.me"]) + assert.Equal(t, "test", data.Upstreams[0].Name) + assert.Equal(t, "roundrobin", data.Upstreams[0].Type) + + fmt.Println(data.Routes) + + // Route + assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) + for _, route := range data.Routes { + switch route.Name { + case "test_customer": + assert.Contains(t, route.Uris, "/customer") + assert.Contains(t, route.Methods, "GET", "GET") + assert.Equal(t, entity.Status(0), route.Status) + case "test_customers": + assert.Contains(t, route.Uris, "/customers") + assert.Contains(t, route.Methods, "GET") + assert.Equal(t, entity.Status(0), route.Status) + case "test_customer-customer_id": + assert.Contains(t, route.Uris, "/customer/*") + assert.Contains(t, route.Methods, "PUT", "DELETE") + assert.Equal(t, entity.Status(0), route.Status) + } + } +} diff --git a/api/test/testdata/import/Postman-API101.yaml b/api/test/testdata/import/Postman-API101.yaml new file mode 100644 index 0000000000..07dd452e49 --- /dev/null +++ b/api/test/testdata/import/Postman-API101.yaml @@ -0,0 +1,158 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# If you want to set the specified configuration value, you can set the new +# in this file. For example if you want to specify the etcd address: +# + +### +# This is the definition file for the Postman API101 sample. +# It is converted from Postman Collection using postman2openapi. + +openapi: 3.0.0 +info: + title: API 101 + description: >- + API 101 template for learning API request basics. Follow along with the + webinar / video or just open the first request and hit **Send**! + version: 1.0.0 +servers: + - url: https://api-101.glitch.me + - url: http://{{apiurl}} +components: + securitySchemes: + apikeyAuth: + type: http + scheme: apikey +paths: + /customers: + get: + tags: + - default + summary: Get all customers + parameters: + - name: user-id + in: header + schema: + type: string + example: '{{userId}}' + responses: + '200': + description: Successful response + content: + application/json: {} + /customer: + get: + tags: + - default + summary: Get one customer + parameters: + - name: user-id + in: header + schema: + type: string + example: '{{userId}}' + - name: id + in: query + schema: + type: integer + example: '1' + responses: + '200': + description: Successful response + content: + application/json: {} + post: + tags: + - default + summary: Add new customer + requestBody: + content: + application/json: + schema: + type: object + example: + name: Dorothy Zborna + type: Individual + security: + - apikeyAuth: [] + parameters: + - name: user-id + in: header + schema: + type: string + example: '{{userId}}' + responses: + '200': + description: Successful response + content: + application/json: {} + /customer/{customer_id}: + put: + tags: + - default + summary: Update customer + requestBody: + content: + application/json: + schema: + type: object + example: + name: Sophia Petrillo + type: Individual + security: + - apikeyAuth: [] + parameters: + - name: user-id + in: header + schema: + type: string + example: '{{userId}}' + - name: customer_id + in: path + schema: + type: integer + required: true + example: '1311' + responses: + '200': + description: Successful response + content: + application/json: {} + delete: + tags: + - default + summary: Remove customer + security: + - apikeyAuth: [] + parameters: + - name: user-id + in: header + schema: + type: string + example: '{{userId}}' + - name: customer_id + in: path + schema: + type: integer + required: true + example: '1310' + responses: + '200': + description: Successful response + content: + application/json: {} + From 2c3d9302717e4a54d729af78af01d67ea6320165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 12:41:18 +0800 Subject: [PATCH 04/23] chore: add license --- .../data_loader/loader/openapi3/import_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index 1175e14883..a1951e7c9a 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package openapi3 import ( From ed2aed13b0ed55f67202d26ddf9fb9f1324ac373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 12:59:23 +0800 Subject: [PATCH 05/23] fix: lint --- api/test/testdata/import/Postman-API101.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/api/test/testdata/import/Postman-API101.yaml b/api/test/testdata/import/Postman-API101.yaml index 07dd452e49..1f0467d409 100644 --- a/api/test/testdata/import/Postman-API101.yaml +++ b/api/test/testdata/import/Postman-API101.yaml @@ -14,14 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# If you want to set the specified configuration value, you can set the new -# in this file. For example if you want to specify the etcd address: -# - ### # This is the definition file for the Postman API101 sample. # It is converted from Postman Collection using postman2openapi. - openapi: 3.0.0 info: title: API 101 @@ -155,4 +150,3 @@ paths: description: Successful response content: application/json: {} - From 8f488df157f65cecf34b9f4748f7806811693fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 12:59:48 +0800 Subject: [PATCH 06/23] fix: code style --- .../handler/data_loader/loader/openapi3/import_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index a1951e7c9a..944cde61cb 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -17,7 +17,6 @@ package openapi3 import ( - "fmt" "io/ioutil" "testing" @@ -89,8 +88,6 @@ func TestParseAPI101Merge(t *testing.T) { assert.Equal(t, "test", data.Upstreams[0].Name) assert.Equal(t, "roundrobin", data.Upstreams[0].Type) - fmt.Println(data.Routes) - // Route assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) for _, route := range data.Routes { From 2658bf2cd605291d8717f0aad6813ed9c0415ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 15:08:46 +0800 Subject: [PATCH 07/23] feat: add authentication plugin mapping --- api/internal/handler/data_loader/loader/openapi3/openapi3.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/openapi3.go b/api/internal/handler/data_loader/loader/openapi3/openapi3.go index 3bce52399a..1cbf35a4b7 100644 --- a/api/internal/handler/data_loader/loader/openapi3/openapi3.go +++ b/api/internal/handler/data_loader/loader/openapi3/openapi3.go @@ -37,5 +37,8 @@ type PathValue struct { } var ( - regURIVar = regexp.MustCompile(`{[\w.]*}`) + regURIVar = regexp.MustCompile(`{[\w.]*}`) + authenticationMappings = map[string]string{ + "apikey": "key-auth", + } ) From 2e8a99c7b88aee88097da711ab6926baa1b5b7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 15:13:06 +0800 Subject: [PATCH 08/23] feat: add apikey plugin support --- .../data_loader/loader/openapi3/import.go | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 681172d962..4a95886593 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -65,8 +65,23 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) globalUpstreamID = o.TaskName // global uri prefix globalPath = "" + + // authentication plugins supported by APISIX + interestedAuthentication = false + interestedAuthenticationList = make(map[string]*openapi3.SecuritySchemeRef) ) + // check securitySchemes settings + if len(s.Components.SecuritySchemes) > 0 { + for name, security := range s.Components.SecuritySchemes { + if strings.ToLower(security.Value.Type) == "http" && + strings.ToLower(security.Value.Scheme) == "apikey" { + interestedAuthentication = true + interestedAuthenticationList[name] = security + } + } + } + // create upstream when servers field not empty if len(s.Servers) > 0 { var upstream entity.Upstream @@ -99,17 +114,37 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) route.Uris = []string{globalPath + realUri} route.Methods = []string{strings.ToUpper(method)} route.UpstreamID = globalUpstreamID + + // Processing plugins in non-method merge mode + // In method merge mode, different methods may have different authentication + // methods, so no plugins are attached to them. + if interestedAuthentication && + operation.Security != nil && + len(*operation.Security) > 0 { + attachAuthenticationPlugin(route, interestedAuthenticationList, operation.Security) + } data.Routes = append(data.Routes, route) } } } - return data, nil } +func attachAuthenticationPlugin(route entity.Route, refs map[string]*openapi3.SecuritySchemeRef, requirements *openapi3.SecurityRequirements) { + for _, requirement := range *requirements { + for name := range requirement { + // Implement authentication method filter + // currently supported: apikey + if _, ok := refs[name]; ok { + route.Plugins[authenticationMappings[name]] = map[string]interface{}{} + } + } + } +} + // Generate APISIX upstream from OpenAPI servers field // return upstream and uri prefix -// Tips: select only first server in servers field +// Tips: It will use only the first server in servers array func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (entity.Upstream, string) { upstream := entity.Upstream{ BaseInfo: entity.BaseInfo{ID: upstreamID}, From 6747450f1e31b70a228aaece50567a9a671a6528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 1 Jun 2022 15:14:49 +0800 Subject: [PATCH 09/23] test: add apikey plugin cases --- api/internal/handler/data_loader/loader/openapi3/import_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index 944cde61cb..8606cf83a0 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -60,6 +60,7 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT") assert.Equal(t, "Update customer", route.Desc) + assert.Len(t, route.Plugins, 1) assert.Equal(t, entity.Status(0), route.Status) case "test_customer-customer_id_DELETE": assert.Contains(t, route.Uris, "/customer/*") @@ -103,6 +104,7 @@ func TestParseAPI101Merge(t *testing.T) { case "test_customer-customer_id": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT", "DELETE") + assert.Len(t, route.Plugins, 0) assert.Equal(t, entity.Status(0), route.Status) } } From a4adf65ad92956c7bde586fa9ff26a380bac48b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Thu, 2 Jun 2022 15:12:47 +0800 Subject: [PATCH 10/23] chore: optimize code --- .../handler/data_loader/loader/openapi3/import.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 4a95886593..138cedff26 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -41,7 +41,7 @@ func (o Loader) Import(input interface{}) (*loader.DataSets, error) { } // no paths in OAS3 document - if len(swagger.Paths) < 1 { + if len(swagger.Paths) <= 0 { return nil, consts.ErrImportFile } @@ -96,7 +96,7 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) // generate route name routeID := o.TaskName + "_" + strings.NewReplacer("/", "-", "{", "", "}", "").Replace(strings.TrimPrefix(uri, "/")) - // decide whether to merge multi method routes based on configuration + // decide whether to merge multi-method routes based on configuration if o.MergeMethod { // create a single route for each path, merge all methods route := generateBaseRoute(routeID, v.Summary) @@ -130,11 +130,12 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) return data, nil } +// Adding authentication plugins to route based on the security scheme +// Currently supported: apikey func attachAuthenticationPlugin(route entity.Route, refs map[string]*openapi3.SecuritySchemeRef, requirements *openapi3.SecurityRequirements) { for _, requirement := range *requirements { for name := range requirement { - // Implement authentication method filter - // currently supported: apikey + // Only add plugins that we can currently handle if _, ok := refs[name]; ok { route.Plugins[authenticationMappings[name]] = map[string]interface{}{} } From 48346431ddf8806ce515380ec00d23518902fb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Fri, 10 Jun 2022 16:50:21 +0800 Subject: [PATCH 11/23] feat: update route name generator rule --- .../data_loader/loader/openapi3/import.go | 17 ++++++++--------- .../data_loader/loader/openapi3/import_test.go | 8 +++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 138cedff26..788671266c 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -93,13 +93,13 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) for uri, v := range s.Paths { // replace parameter in uri to wildcard realUri := regURIVar.ReplaceAllString(uri, "*") - // generate route name - routeID := o.TaskName + "_" + strings.NewReplacer("/", "-", "{", "", "}", "").Replace(strings.TrimPrefix(uri, "/")) + // generate route Name + routeName := o.TaskName + "_" + strings.TrimPrefix(uri, "/") // decide whether to merge multi-method routes based on configuration if o.MergeMethod { // create a single route for each path, merge all methods - route := generateBaseRoute(routeID, v.Summary) + route := generateBaseRoute(routeName, v.Summary) route.Uris = []string{globalPath + realUri} route.UpstreamID = globalUpstreamID for method := range v.Operations() { @@ -109,7 +109,7 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) } else { // create routes for each method of each path for method, operation := range v.Operations() { - subRouteID := routeID + "_" + method + subRouteID := routeName + "_" + method route := generateBaseRoute(subRouteID, operation.Summary) route.Uris = []string{globalPath + realUri} route.Methods = []string{strings.ToUpper(method)} @@ -170,11 +170,10 @@ func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (ent } // Generate a base route for customize -func generateBaseRoute(id string, desc string) entity.Route { +func generateBaseRoute(name string, desc string) entity.Route { return entity.Route{ - BaseInfo: entity.BaseInfo{ID: id}, - Name: id, - Desc: desc, - Plugins: make(map[string]interface{}), + Name: name, + Desc: desc, + Plugins: make(map[string]interface{}), } } diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index 8606cf83a0..feef13463e 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -17,6 +17,7 @@ package openapi3 import ( + "fmt" "io/ioutil" "testing" @@ -56,13 +57,13 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Contains(t, route.Methods, "GET") assert.Equal(t, "Get one customer", route.Desc) assert.Equal(t, entity.Status(0), route.Status) - case "test_customer-customer_id_PUT": + case "test_customer/{customer_id}_PUT": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT") assert.Equal(t, "Update customer", route.Desc) assert.Len(t, route.Plugins, 1) assert.Equal(t, entity.Status(0), route.Status) - case "test_customer-customer_id_DELETE": + case "test_customer/{customer_id}_DELETE": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "DELETE") assert.Equal(t, "Remove customer", route.Desc) @@ -92,6 +93,7 @@ func TestParseAPI101Merge(t *testing.T) { // Route assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) for _, route := range data.Routes { + fmt.Println(route.Name) switch route.Name { case "test_customer": assert.Contains(t, route.Uris, "/customer") @@ -101,7 +103,7 @@ func TestParseAPI101Merge(t *testing.T) { assert.Contains(t, route.Uris, "/customers") assert.Contains(t, route.Methods, "GET") assert.Equal(t, entity.Status(0), route.Status) - case "test_customer-customer_id": + case "test_customer/{customer_id}": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT", "DELETE") assert.Len(t, route.Plugins, 0) From 88f5d21e08ee7d71d8753d4339ae6cb3d1c79011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 14 Jun 2022 10:29:37 +0800 Subject: [PATCH 12/23] chore: remove debug line --- api/internal/handler/data_loader/loader/openapi3/import_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index feef13463e..096e917e7d 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -17,7 +17,6 @@ package openapi3 import ( - "fmt" "io/ioutil" "testing" @@ -93,7 +92,6 @@ func TestParseAPI101Merge(t *testing.T) { // Route assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) for _, route := range data.Routes { - fmt.Println(route.Name) switch route.Name { case "test_customer": assert.Contains(t, route.Uris, "/customer") From 2d04feb9d10ab8cc5076a90c6fa332de0e48b482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 15 Jun 2022 17:07:46 +0800 Subject: [PATCH 13/23] feat: remove plugins attach --- .../data_loader/loader/openapi3/import.go | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 788671266c..f82d8231a4 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -65,23 +65,8 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) globalUpstreamID = o.TaskName // global uri prefix globalPath = "" - - // authentication plugins supported by APISIX - interestedAuthentication = false - interestedAuthenticationList = make(map[string]*openapi3.SecuritySchemeRef) ) - // check securitySchemes settings - if len(s.Components.SecuritySchemes) > 0 { - for name, security := range s.Components.SecuritySchemes { - if strings.ToLower(security.Value.Type) == "http" && - strings.ToLower(security.Value.Scheme) == "apikey" { - interestedAuthentication = true - interestedAuthenticationList[name] = security - } - } - } - // create upstream when servers field not empty if len(s.Servers) > 0 { var upstream entity.Upstream @@ -114,15 +99,6 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) route.Uris = []string{globalPath + realUri} route.Methods = []string{strings.ToUpper(method)} route.UpstreamID = globalUpstreamID - - // Processing plugins in non-method merge mode - // In method merge mode, different methods may have different authentication - // methods, so no plugins are attached to them. - if interestedAuthentication && - operation.Security != nil && - len(*operation.Security) > 0 { - attachAuthenticationPlugin(route, interestedAuthenticationList, operation.Security) - } data.Routes = append(data.Routes, route) } } @@ -130,19 +106,6 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) return data, nil } -// Adding authentication plugins to route based on the security scheme -// Currently supported: apikey -func attachAuthenticationPlugin(route entity.Route, refs map[string]*openapi3.SecuritySchemeRef, requirements *openapi3.SecurityRequirements) { - for _, requirement := range *requirements { - for name := range requirement { - // Only add plugins that we can currently handle - if _, ok := refs[name]; ok { - route.Plugins[authenticationMappings[name]] = map[string]interface{}{} - } - } - } -} - // Generate APISIX upstream from OpenAPI servers field // return upstream and uri prefix // Tips: It will use only the first server in servers array From 4202db8e75fc6bcac541d3e4397072e5e4382920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Wed, 15 Jun 2022 17:29:03 +0800 Subject: [PATCH 14/23] test: remove plugin attach cases --- api/internal/handler/data_loader/loader/openapi3/import_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index 096e917e7d..dce5409722 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -60,7 +60,6 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT") assert.Equal(t, "Update customer", route.Desc) - assert.Len(t, route.Plugins, 1) assert.Equal(t, entity.Status(0), route.Status) case "test_customer/{customer_id}_DELETE": assert.Contains(t, route.Uris, "/customer/*") @@ -104,7 +103,6 @@ func TestParseAPI101Merge(t *testing.T) { case "test_customer/{customer_id}": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT", "DELETE") - assert.Len(t, route.Plugins, 0) assert.Equal(t, entity.Status(0), route.Status) } } From 6f4abe9370933c14a85b5526310d9c1e47156b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Thu, 16 Jun 2022 14:45:07 +0800 Subject: [PATCH 15/23] feat: add byte assert --- .../handler/data_loader/loader/openapi3/import.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index f82d8231a4..e30fc5cf2b 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -18,7 +18,9 @@ package openapi3 import ( "errors" + "fmt" "net/url" + "reflect" "strings" "time" @@ -34,8 +36,13 @@ func (o Loader) Import(input interface{}) (*loader.DataSets, error) { return nil, errors.New("input is nil") } + d, ok := input.([]byte) + if !ok { + return nil, fmt.Errorf("input format error: expected []byte but it is %s", reflect.TypeOf(input).Kind().String()) + } + // load OAS3 document - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(input.([]byte)) + swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(d) if err != nil { return nil, err } From 574bf6c7582424e75ca3fa00512410b349c5b6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 10:06:05 +0800 Subject: [PATCH 16/23] feat: add error warp for more context --- api/go.mod | 1 + api/go.sum | 5 ----- api/internal/handler/data_loader/loader/openapi3/import.go | 7 +++---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/api/go.mod b/api/go.mod index 691ecfc074..d45f9a96b9 100644 --- a/api/go.mod +++ b/api/go.mod @@ -22,6 +22,7 @@ require ( github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.8.0 // indirect github.com/satori/go.uuid v1.2.0 github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b diff --git a/api/go.sum b/api/go.sum index 38aa782bd6..19d50c6ee1 100644 --- a/api/go.sum +++ b/api/go.sum @@ -271,7 +271,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -358,9 +357,7 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= @@ -881,7 +878,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= @@ -889,7 +885,6 @@ gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWd gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index e30fc5cf2b..55c4e08eff 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -17,14 +17,13 @@ package openapi3 import ( - "errors" - "fmt" "net/url" "reflect" "strings" "time" "github.com/getkin/kin-openapi/openapi3" + "github.com/pkg/errors" "github.com/apisix/manager-api/internal/core/entity" "github.com/apisix/manager-api/internal/handler/data_loader/loader" @@ -38,7 +37,7 @@ func (o Loader) Import(input interface{}) (*loader.DataSets, error) { d, ok := input.([]byte) if !ok { - return nil, fmt.Errorf("input format error: expected []byte but it is %s", reflect.TypeOf(input).Kind().String()) + return nil, errors.Errorf("input format error: expected []byte but it is %s", reflect.TypeOf(input).Kind().String()) } // load OAS3 document @@ -49,7 +48,7 @@ func (o Loader) Import(input interface{}) (*loader.DataSets, error) { // no paths in OAS3 document if len(swagger.Paths) <= 0 { - return nil, consts.ErrImportFile + return nil, errors.Wrap(errors.New("OpenAPI documentation does not contain any paths"), consts.ErrImportFile.Error()) } if o.TaskName == "" { From beaceb56d632282f6e035d8edc9d20cbbdecba9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 10:15:27 +0800 Subject: [PATCH 17/23] test: add default branch for avoid bad route name --- .../handler/data_loader/loader/openapi3/import_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index dce5409722..aa1860a0be 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -66,6 +66,8 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Contains(t, route.Methods, "DELETE") assert.Equal(t, "Remove customer", route.Desc) assert.Equal(t, entity.Status(0), route.Status) + default: + t.Fatal("bad route name exist") } } } @@ -104,6 +106,8 @@ func TestParseAPI101Merge(t *testing.T) { assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT", "DELETE") assert.Equal(t, entity.Status(0), route.Status) + default: + t.Fatal("bad route name exist") } } } From 3e3cada90083638536be2e3b8228ced0d5a39912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 11:16:18 +0800 Subject: [PATCH 18/23] chore: remove plugin mapping --- api/internal/handler/data_loader/loader/openapi3/openapi3.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/openapi3.go b/api/internal/handler/data_loader/loader/openapi3/openapi3.go index 1cbf35a4b7..3bce52399a 100644 --- a/api/internal/handler/data_loader/loader/openapi3/openapi3.go +++ b/api/internal/handler/data_loader/loader/openapi3/openapi3.go @@ -37,8 +37,5 @@ type PathValue struct { } var ( - regURIVar = regexp.MustCompile(`{[\w.]*}`) - authenticationMappings = map[string]string{ - "apikey": "key-auth", - } + regURIVar = regexp.MustCompile(`{[\w.]*}`) ) From bd71046e5e5d4dab528ddc197c98bdd063c7b60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 11:54:29 +0800 Subject: [PATCH 19/23] chore: move program fault error to panic --- api/internal/handler/data_loader/loader/openapi3/import.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 55c4e08eff..8ea3478f3b 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -17,6 +17,7 @@ package openapi3 import ( + "fmt" "net/url" "reflect" "strings" @@ -32,12 +33,12 @@ import ( func (o Loader) Import(input interface{}) (*loader.DataSets, error) { if input == nil { - return nil, errors.New("input is nil") + panic("input is nil") } d, ok := input.([]byte) if !ok { - return nil, errors.Errorf("input format error: expected []byte but it is %s", reflect.TypeOf(input).Kind().String()) + panic(fmt.Sprintf("input format error: expected []byte but it is %s", reflect.TypeOf(input).Kind().String())) } // load OAS3 document From 06144b56b93dc17e821d3251e80bf29fd985f374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 12:25:43 +0800 Subject: [PATCH 20/23] test: fix import test case --- .../handler/data_loader/loader/openapi3/import_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index aa1860a0be..a202979f6c 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -51,11 +51,21 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Equal(t, data.Upstreams[0].ID, data.Routes[0].UpstreamID) for _, route := range data.Routes { switch route.Name { + case "test_customers_GET": + assert.Contains(t, route.Uris, "/customers") + assert.Contains(t, route.Methods, "GET") + assert.Equal(t, "Get all customers", route.Desc) + assert.Equal(t, entity.Status(0), route.Status) case "test_customer_GET": assert.Contains(t, route.Uris, "/customer") assert.Contains(t, route.Methods, "GET") assert.Equal(t, "Get one customer", route.Desc) assert.Equal(t, entity.Status(0), route.Status) + case "test_customer_POST": + assert.Contains(t, route.Uris, "/customer") + assert.Contains(t, route.Methods, "POST") + assert.Equal(t, "Add new customer", route.Desc) + assert.Equal(t, entity.Status(0), route.Status) case "test_customer/{customer_id}_PUT": assert.Contains(t, route.Uris, "/customer/*") assert.Contains(t, route.Methods, "PUT") From 9a10f64dab6c62782a5519513c0e7f8fb4667356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 15:03:52 +0800 Subject: [PATCH 21/23] chore: simplify uri var regexp --- api/internal/handler/data_loader/loader/openapi3/openapi3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/openapi3.go b/api/internal/handler/data_loader/loader/openapi3/openapi3.go index 3bce52399a..f480ff22d3 100644 --- a/api/internal/handler/data_loader/loader/openapi3/openapi3.go +++ b/api/internal/handler/data_loader/loader/openapi3/openapi3.go @@ -37,5 +37,5 @@ type PathValue struct { } var ( - regURIVar = regexp.MustCompile(`{[\w.]*}`) + regURIVar = regexp.MustCompile(`{.*?}`) ) From c879a13fba284ad4ab45ed7faa1f3eae3203af10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 16:09:41 +0800 Subject: [PATCH 22/23] feat: remove servers field feature --- .../data_loader/loader/openapi3/import.go | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import.go b/api/internal/handler/data_loader/loader/openapi3/import.go index 8ea3478f3b..04ecf3bd3b 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import.go +++ b/api/internal/handler/data_loader/loader/openapi3/import.go @@ -18,7 +18,6 @@ package openapi3 import ( "fmt" - "net/url" "reflect" "strings" "time" @@ -77,7 +76,13 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) // create upstream when servers field not empty if len(s.Servers) > 0 { var upstream entity.Upstream - upstream, globalPath = generateUpstreamByServers(s.Servers, globalUpstreamID) + upstream = entity.Upstream{ + BaseInfo: entity.BaseInfo{ID: globalUpstreamID}, + UpstreamDef: entity.UpstreamDef{ + Name: globalUpstreamID, + Type: "roundrobin", + }, + } data.Upstreams = append(data.Upstreams, upstream) } @@ -113,32 +118,6 @@ func (o Loader) convertToEntities(s *openapi3.Swagger) (*loader.DataSets, error) return data, nil } -// Generate APISIX upstream from OpenAPI servers field -// return upstream and uri prefix -// Tips: It will use only the first server in servers array -func generateUpstreamByServers(servers openapi3.Servers, upstreamID string) (entity.Upstream, string) { - upstream := entity.Upstream{ - BaseInfo: entity.BaseInfo{ID: upstreamID}, - UpstreamDef: entity.UpstreamDef{ - Name: upstreamID, - Type: "roundrobin", - }, - } - - u, err := url.Parse(servers[0].URL) - if err != nil { - // return an empty upstream when parsing url failed - return upstream, "" - } - - upstream.Scheme = u.Scheme - upstream.Nodes = map[string]float64{ - u.Host: 1, - } - - return upstream, u.Path -} - // Generate a base route for customize func generateBaseRoute(name string, desc string) entity.Route { return entity.Route{ From 9d3ffd1b129b6cf0c2af1239dd92013e63b7c1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD=E5=B9=B3?= Date: Tue, 21 Jun 2022 16:10:49 +0800 Subject: [PATCH 23/23] test: remove servers field cases --- .../handler/data_loader/loader/openapi3/import_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/internal/handler/data_loader/loader/openapi3/import_test.go b/api/internal/handler/data_loader/loader/openapi3/import_test.go index a202979f6c..bc6ff809c0 100644 --- a/api/internal/handler/data_loader/loader/openapi3/import_test.go +++ b/api/internal/handler/data_loader/loader/openapi3/import_test.go @@ -42,8 +42,6 @@ func TestParseAPI101NoMerge(t *testing.T) { assert.Len(t, data.Upstreams, 1) // Upstream - assert.Equal(t, "https", data.Upstreams[0].Scheme) - assert.Equal(t, float64(1), data.Upstreams[0].Nodes.(map[string]float64)["api-101.glitch.me"]) assert.Equal(t, "test", data.Upstreams[0].Name) assert.Equal(t, "roundrobin", data.Upstreams[0].Type) @@ -95,8 +93,6 @@ func TestParseAPI101Merge(t *testing.T) { assert.Len(t, data.Upstreams, 1) // Upstream - assert.Equal(t, "https", data.Upstreams[0].Scheme) - assert.Equal(t, float64(1), data.Upstreams[0].Nodes.(map[string]float64)["api-101.glitch.me"]) assert.Equal(t, "test", data.Upstreams[0].Name) assert.Equal(t, "roundrobin", data.Upstreams[0].Type)