#2 - add swaggerset #8

Merged
vtolstov merged 3 commits from kgorbunov/servicechecker:#2 into master 2024-12-06 19:01:08 +03:00
4 changed files with 49 additions and 64 deletions
Showing only changes of commit e207117ba4 - Show all commits

View File

@ -7,7 +7,6 @@ import (
"os/signal" "os/signal"
"time" "time"
openapi_v3 "github.com/google/gnostic/openapiv3"
"github.com/google/uuid" "github.com/google/uuid"
grpccli "go.unistack.org/micro-client-grpc/v3" grpccli "go.unistack.org/micro-client-grpc/v3"
httpcli "go.unistack.org/micro-client-http/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/httpconn"
"go.unistack.org/servicechecker/pkg/protoset" "go.unistack.org/servicechecker/pkg/protoset"
"go.unistack.org/servicechecker/pkg/scheduler" "go.unistack.org/servicechecker/pkg/scheduler"
"go.unistack.org/servicechecker/pkg/swaggerset"
) )
var ( var (
@ -213,8 +213,10 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri
var treq client.Request var treq client.Request
var opts []client.CallOption var opts []client.CallOption
var labels []string var labels []string
swaggerSet := swaggerset.NewSwaggerSet()
if task.HTTP.OpenAPI != "" { if task.HTTP.OpenAPI != "" {
var svc string
openapiBuf, err := os.ReadFile(task.HTTP.OpenAPI) openapiBuf, err := os.ReadFile(task.HTTP.OpenAPI)
if err != nil { if err != nil {
@ -222,12 +224,11 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri
return nil, nil, err return nil, nil, err
} }
doc, err := openapi_v3.ParseDocument(openapiBuf) err = swaggerSet.AddSwaggerset(task.HTTP.Addr, svc, openapiBuf)
if err != nil { 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 return nil, nil, err
} }
_ = doc
errmap := make(map[string]interface{}, 1) errmap := make(map[string]interface{}, 1)
errmap["default"] = &codecpb.Frame{} 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"), // 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)} 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) treq = c.NewRequest(task.Name, task.Name, req)

View File

@ -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 == "" { if svc == "" || mth == "" || addr == "" || typereq == "" {
return nil, errors.New("addr or service name is empty") return nil, errors.New("addr or service name is empty")
} }
messages := newMessages()
p.mu.Lock() p.mu.Lock()
doc := p.files[addr+"|"+svc] doc := p.files[addr+"|"+svc]
p.mu.Unlock() p.mu.Unlock()
pathItem := doc.Paths.Value(mth) pathItem := doc.Paths.Value(mth)
if pathItem.Get != nil { reqParam, reqBody, rsp := handleOperation(typereq, pathItem.Get)
reqParam, reqBody, rsp := handleOperation("GET", pathItem.Get) msg := &Message{
messages.Msgs = append(messages.Msgs, message{ Type: typereq,
Type: "GET", Request: httpRequest{
RequestParam: reqParam, Header: reqParam,
RequestBody: reqBody, Body: reqBody,
Response: rsp, },
}) 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,
})
} }
return messages, nil return msg, nil
} }
func (p *SwaggerSet) AddSwaggerset(addr, svc string, data []byte) error { func (p *SwaggerSet) AddSwaggerset(addr, svc string, data []byte) error {

View File

@ -21,7 +21,7 @@ func TestSwaggerSet_1(t *testing.T) {
"localhost:8080", "localhost:8080",
"service", "service",
"/domain-service/v1/push_mail/enabled", "/domain-service/v1/push_mail/enabled",
"POST") []string{"POST", "GET"})
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, msgs.Msgs) assert.NotNil(t, msgs.Msgs)
for _, msg := range msgs.Msgs { for _, msg := range msgs.Msgs {
@ -31,6 +31,7 @@ func TestSwaggerSet_1(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
rsp, err := json.Marshal(msg.Response) rsp, err := json.Marshal(msg.Response)
assert.Nil(t, err) 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)
} }
} }

View File

@ -1,7 +1,6 @@
package swaggerset package swaggerset
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -10,24 +9,17 @@ import (
dynamicstruct "github.com/ompluscator/dynamic-struct" dynamicstruct "github.com/ompluscator/dynamic-struct"
) )
type messages struct { type Message struct {
Msgs []message Type string
Request httpRequest
Response interface{}
} }
func newMessages() *messages { type httpRequest struct {
return &messages{ Header interface{}
Msgs: make([]message, 0), 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{}) { func handleOperation(method string, operation *openapi3.Operation) (reqParam, reqBody, rsp interface{}) {
fmt.Printf(" Method: %s\n", method) fmt.Printf(" Method: %s\n", method)
@ -37,23 +29,21 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re
for _, paramRef := range operation.Parameters { for _, paramRef := range operation.Parameters {
param := paramRef.Value param := paramRef.Value
fieldName := capitalize(param.Name) fieldName := capitalize(param.Name)
jsonName := strings.ToLower(param.Name[:1]) + param.Name[1:]
goType := getGoType(param.Schema) goType := getGoType(param.Schema)
// В зависимости от того, где параметр находится (header, query, path, etc.), добавляем соответствующий тег // В зависимости от того, где параметр находится (header, query, path, etc.), добавляем соответствующий тег
switch param.In { switch param.In {
case "query": 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": 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: 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() // Получили структуру запроса для методов, где есть параметры в header, query, path, etc., добавили теги
fmt.Printf(" Request (Parameters): %+v\n", requestStruct) reqParam = paramsStruct.Build().New()
buf, _ := json.Marshal(requestStruct)
fmt.Printf(" Request (Parameters) JSON: %s\n", buf)
reqParam = requestStruct
} }
// Обработка тела запроса (POST) // Обработка тела запроса (POST)
@ -62,9 +52,7 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re
bodyFields := buildDynamicStruct(content.Schema) bodyFields := buildDynamicStruct(content.Schema)
bodyStruct := reflect.StructOf(bodyFields) bodyStruct := reflect.StructOf(bodyFields)
bodyInstance := reflect.New(bodyStruct).Interface() 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 reqBody = bodyInstance
} }
} }
@ -76,9 +64,7 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re
responseFields := buildDynamicStruct(content.Schema) responseFields := buildDynamicStruct(content.Schema)
responseStruct := reflect.StructOf(responseFields) responseStruct := reflect.StructOf(responseFields)
responseInstance := reflect.New(responseStruct).Interface() responseInstance := reflect.New(responseStruct).Interface()
fmt.Printf(" Response: %+v\n", responseInstance) // Получили структуру ответа
buf, _ := json.Marshal(responseInstance)
fmt.Printf(" Response JSON: %s\n", buf)
rsp = responseInstance rsp = responseInstance
} }
} }
@ -88,11 +74,10 @@ func handleOperation(method string, operation *openapi3.Operation) (reqParam, re
// Рекурсивное создание структуры из схемы с учетом $ref // Рекурсивное создание структуры из схемы с учетом $ref
func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField { func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField {
var builder []reflect.StructField var sfields []reflect.StructField
fmt.Println("ref: ", schema.Ref)
if len(schema.Value.Properties) == 0 { if len(schema.Value.Properties) == 0 {
return builder return sfields
} }
for name, prop := range schema.Value.Properties { 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)), Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)),
} }
builder = append(builder, sfield) sfields = append(sfields, sfield)
} else { } else {
sfield := reflect.StructField{ sfield := reflect.StructField{
Name: fieldName, Name: fieldName,
@ -114,11 +99,11 @@ func buildDynamicStruct(schema *openapi3.SchemaRef) []reflect.StructField {
Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)), Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)),
} }
builder = append(builder, sfield) sfields = append(sfields, sfield)
} }
} }
return builder return sfields
} }
// Преобразование типа OpenAPI в тип Go // Преобразование типа OpenAPI в тип Go
@ -152,7 +137,7 @@ func capitalize(fieldName string) string {
// Обрабатываем каждый фрагмент // Обрабатываем каждый фрагмент
for i := 0; i < len(parts); i++ { 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])
} }
// Собираем строку обратно, соединяя части без подчеркиваний // Собираем строку обратно, соединяя части без подчеркиваний