From e207117ba4391f1c71ff5f71dbd2bf65bdd2f1ef Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Sat, 30 Nov 2024 19:30:46 +0300 Subject: [PATCH] #2 - add swaggerset --- cmd/servicechecker/main.go | 20 ++++++++--- pkg/swaggerset/swaggerset.go | 31 ++++++----------- pkg/swaggerset/swaggerset_test.go | 5 +-- pkg/swaggerset/util.go | 57 ++++++++++++------------------- 4 files changed, 49 insertions(+), 64 deletions(-) diff --git a/cmd/servicechecker/main.go b/cmd/servicechecker/main.go index fc40ce5..6455cbd 100644 --- a/cmd/servicechecker/main.go +++ b/cmd/servicechecker/main.go @@ -7,7 +7,6 @@ import ( "os/signal" "time" - openapi_v3 "github.com/google/gnostic/openapiv3" "github.com/google/uuid" grpccli "go.unistack.org/micro-client-grpc/v3" httpcli "go.unistack.org/micro-client-http/v3" @@ -31,6 +30,7 @@ import ( "go.unistack.org/servicechecker/pkg/httpconn" "go.unistack.org/servicechecker/pkg/protoset" "go.unistack.org/servicechecker/pkg/scheduler" + "go.unistack.org/servicechecker/pkg/swaggerset" ) var ( @@ -213,8 +213,10 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri var treq client.Request var opts []client.CallOption var labels []string + swaggerSet := swaggerset.NewSwaggerSet() if task.HTTP.OpenAPI != "" { + var svc string openapiBuf, err := os.ReadFile(task.HTTP.OpenAPI) if err != nil { @@ -222,12 +224,11 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri return nil, nil, err } - doc, err := openapi_v3.ParseDocument(openapiBuf) + err = swaggerSet.AddSwaggerset(task.HTTP.Addr, svc, openapiBuf) if err != nil { - l.Error(ctx, "failed to unmarshal openapi file", err) + l.Error(ctx, "failed to add openApi spec", err) return nil, nil, err } - _ = doc errmap := make(map[string]interface{}, 1) errmap["default"] = &codecpb.Frame{} @@ -239,8 +240,17 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri // client.WithContentType("application/json"), } + msg, err := swaggerSet.GetMessage(task.HTTP.Addr, svc, task.HTTP.Method, "POST") + if err != nil { + l.Error(ctx, "failed to get message from swagger spec", err) + return nil, nil, err + } + req = &codecpb.Frame{Data: []byte(task.HTTP.Data)} - rsp = &codecpb.Frame{} + if task.HTTP.Data == "" { + req = msg.Request + } + rsp = msg.Response treq = c.NewRequest(task.Name, task.Name, req) diff --git a/pkg/swaggerset/swaggerset.go b/pkg/swaggerset/swaggerset.go index 167fc0e..d3553ca 100644 --- a/pkg/swaggerset/swaggerset.go +++ b/pkg/swaggerset/swaggerset.go @@ -23,38 +23,27 @@ func NewSwaggerSet() *SwaggerSet { } } -func (p *SwaggerSet) GetMessage(addr, svc, mth, typereq string) (*messages, error) { +func (p *SwaggerSet) GetMessage(addr, svc, mth string, typereq string) (*Message, error) { if svc == "" || mth == "" || addr == "" || typereq == "" { return nil, errors.New("addr or service name is empty") } - messages := newMessages() - p.mu.Lock() doc := p.files[addr+"|"+svc] p.mu.Unlock() pathItem := doc.Paths.Value(mth) - if pathItem.Get != nil { - reqParam, reqBody, rsp := handleOperation("GET", pathItem.Get) - messages.Msgs = append(messages.Msgs, message{ - Type: "GET", - RequestParam: reqParam, - RequestBody: reqBody, - Response: rsp, - }) - } - if pathItem.Post != nil { - reqParam, reqBody, rsp := handleOperation("POST", pathItem.Post) - messages.Msgs = append(messages.Msgs, message{ - Type: "POST", - RequestParam: reqParam, - RequestBody: reqBody, - Response: rsp, - }) + reqParam, reqBody, rsp := handleOperation(typereq, pathItem.Get) + msg := &Message{ + Type: typereq, + Request: httpRequest{ + Header: reqParam, + Body: reqBody, + }, + Response: rsp, } - return messages, nil + return msg, nil } func (p *SwaggerSet) AddSwaggerset(addr, svc string, data []byte) error { diff --git a/pkg/swaggerset/swaggerset_test.go b/pkg/swaggerset/swaggerset_test.go index ec80c78..cd97ced 100644 --- a/pkg/swaggerset/swaggerset_test.go +++ b/pkg/swaggerset/swaggerset_test.go @@ -21,7 +21,7 @@ func TestSwaggerSet_1(t *testing.T) { "localhost:8080", "service", "/domain-service/v1/push_mail/enabled", - "POST") + []string{"POST", "GET"}) assert.Nil(t, err) assert.NotNil(t, msgs.Msgs) for _, msg := range msgs.Msgs { @@ -31,6 +31,7 @@ func TestSwaggerSet_1(t *testing.T) { assert.Nil(t, err) rsp, err := json.Marshal(msg.Response) assert.Nil(t, err) - fmt.Printf("type: %s, reqParam: %s, reqBody: %s, rsp: %s \n", msg.Type, reqParam, reqBody, rsp) + fmt.Printf("JSON: type: %s, reqParam: %s, reqBody: %s, rsp: %s \n", msg.Type, reqParam, reqBody, rsp) + fmt.Printf("Struct: type: %s, reqParam: %+v, reqBody: %+v, rsp: %+v \n", msg.Type, msg.RequestParam, msg.RequestBody, msg.Response) } } diff --git a/pkg/swaggerset/util.go b/pkg/swaggerset/util.go index cb2049c..2953f94 100644 --- a/pkg/swaggerset/util.go +++ b/pkg/swaggerset/util.go @@ -1,7 +1,6 @@ package swaggerset import ( - "encoding/json" "fmt" "reflect" "strings" @@ -10,24 +9,17 @@ import ( dynamicstruct "github.com/ompluscator/dynamic-struct" ) -type messages struct { - Msgs []message +type Message struct { + Type string + Request httpRequest + Response interface{} } -func newMessages() *messages { - return &messages{ - Msgs: make([]message, 0), - } +type httpRequest struct { + Header interface{} + Body interface{} } -type message struct { - Type string - RequestParam interface{} - RequestBody interface{} - Response interface{} -} - -// Обработка операции (GET или POST) func handleOperation(method string, operation *openapi3.Operation) (reqParam, reqBody, rsp interface{}) { fmt.Printf(" Method: %s\n", method) @@ -37,23 +29,21 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re for _, paramRef := range operation.Parameters { param := paramRef.Value fieldName := capitalize(param.Name) + jsonName := strings.ToLower(param.Name[:1]) + param.Name[1:] goType := getGoType(param.Schema) // В зависимости от того, где параметр находится (header, query, path, etc.), добавляем соответствующий тег switch param.In { case "query": - paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s" query:"%s"`, param.Name, param.Name)) + paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s" query:"%s"`, jsonName, jsonName)) case "header": - paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s" header:"%s"`, param.Name, param.Name)) + paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s" header:"%s"`, jsonName, jsonName)) default: - paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s"`, param.Name)) + paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s"`, jsonName)) } } - requestStruct := paramsStruct.Build().New() - fmt.Printf(" Request (Parameters): %+v\n", requestStruct) - buf, _ := json.Marshal(requestStruct) - fmt.Printf(" Request (Parameters) JSON: %s\n", buf) - reqParam = requestStruct + // Получили структуру запроса для методов, где есть параметры в header, query, path, etc., добавили теги + reqParam = paramsStruct.Build().New() } // Обработка тела запроса (POST) @@ -62,9 +52,7 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re bodyFields := buildDynamicStruct(content.Schema) bodyStruct := reflect.StructOf(bodyFields) bodyInstance := reflect.New(bodyStruct).Interface() - fmt.Printf(" Request (Body): %+v\n", bodyInstance) - buf, _ := json.Marshal(bodyInstance) - fmt.Printf(" Request (Body) JSON: %s\n", buf) + // Получили тело запроса reqBody = bodyInstance } } @@ -76,9 +64,7 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re responseFields := buildDynamicStruct(content.Schema) responseStruct := reflect.StructOf(responseFields) responseInstance := reflect.New(responseStruct).Interface() - fmt.Printf(" Response: %+v\n", responseInstance) - buf, _ := json.Marshal(responseInstance) - fmt.Printf(" Response JSON: %s\n", buf) + // Получили структуру ответа rsp = responseInstance } } @@ -88,11 +74,10 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re // Рекурсивное создание структуры из схемы с учетом $ref func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField { - var builder []reflect.StructField - fmt.Println("ref: ", schema.Ref) + var sfields []reflect.StructField if len(schema.Value.Properties) == 0 { - return builder + return sfields } for name, prop := range schema.Value.Properties { @@ -106,7 +91,7 @@ func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField { Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)), } - builder = append(builder, sfield) + sfields = append(sfields, sfield) } else { sfield := reflect.StructField{ Name: fieldName, @@ -114,11 +99,11 @@ func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField { Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)), } - builder = append(builder, sfield) + sfields = append(sfields, sfield) } } - return builder + return sfields } // Преобразование типа OpenAPI в тип Go @@ -152,7 +137,7 @@ func capitalize(fieldName string) string { // Обрабатываем каждый фрагмент for i := 0; i < len(parts); i++ { // Капитализируем первые буквы всех частей, кроме первой - parts[i] = strings.Title(parts[i]) + parts[i] = strings.Title(parts[i]) //cases.Title(language.English).String(parts[i]) } // Собираем строку обратно, соединяя части без подчеркиваний