package swaggerset import ( "fmt" "reflect" "strings" dynamicstruct "github.com/ompluscator/dynamic-struct" openapi "go.unistack.org/micro-proto/v4/openapiv3" ) type Message struct { Type string Request httpRequest Response interface{} } type httpRequest struct { Header interface{} Body interface{} } func handleOperation(operation *openapi.Operation, doc *openapi.Document) (reqParam, reqBody, rsp interface{}) { // Обработка параметров (GET) if len(operation.Parameters) > 0 { paramsStruct := dynamicstruct.NewStruct() for _, paramRef := range operation.Parameters { var param *openapi.Parameter param = paramRef.GetParameter() fieldName := capitalize(param.Name) jsonName := strings.ToLower(param.Name[:1]) + param.Name[1:] goType := getGoType(doc, param.Schema) // В зависимости от того, где параметр находится (header, query, path, etc.), добавляем соответствующий тег switch param.In { case "query": 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"`, jsonName, jsonName)) default: paramsStruct = paramsStruct.AddField(fieldName, goType, fmt.Sprintf(`json:"%s"`, jsonName)) } } // Получили структуру запроса для методов, где есть параметры в header, query, path, etc., добавили теги reqParam = paramsStruct.Build().New() } // Обработка тела запроса (POST) if operation.GetRequestBody() != nil { if operation.GetRequestBody().GetRequestBody() != nil { bodyFields := buildDynamicStruct(doc, operation.GetRequestBody().GetRequestBody().GetContent().GetAdditionalProperties()[0].GetValue().GetSchema()) bodyStruct := reflect.StructOf(bodyFields) bodyInstance := reflect.New(bodyStruct).Interface() // Получили тело запроса reqBody = bodyInstance } if operation.GetRequestBody().GetReference() != nil { schemOrRef := findReference(doc, operation.GetRequestBody().GetReference().GetXRef()) bodyFields := buildDynamicStruct(doc, schemOrRef) bodyStruct := reflect.StructOf(bodyFields) bodyInstance := reflect.New(bodyStruct).Interface() // Получили тело запроса reqBody = bodyInstance } } // Обработка ответов for _, rspOrRef := range operation.Responses.ResponseOrReference { if rspOrRef.GetValue().GetResponse() != nil { for _, prop := range rspOrRef.Value.GetResponse().GetContent().GetAdditionalProperties() { responseFields := buildDynamicStruct(doc, prop.GetValue().GetSchema()) responseStruct := reflect.StructOf(responseFields) responseInstance := reflect.New(responseStruct).Interface() // Получили структуру ответа rsp = responseInstance } } if rspOrRef.GetValue().GetReference() != nil { schemaOrRef := findReference(doc, rspOrRef.GetValue().GetReference().GetXRef()) responseFields := buildDynamicStruct(doc, schemaOrRef) responseStruct := reflect.StructOf(responseFields) responseInstance := reflect.New(responseStruct).Interface() // Получили структуру ответа rsp = responseInstance } } return } // Рекурсивное создание структуры из схемы с учетом $ref func buildDynamicStruct(doc *openapi.Document, schemaOrRef *openapi.SchemaOrReference) []reflect.StructField { var sfields []reflect.StructField var schema *openapi.Schema if schemaOrRef.GetSchema() != nil { schema = schemaOrRef.GetSchema() } if schemaOrRef.GetReference() != nil { name := strings.Split(schemaOrRef.GetReference().GetXRef(), "#/components/schemas/")[1] fieldName := capitalize(name) subBuilder := buildDynamicStruct(doc, findReference(doc, schemaOrRef.GetReference().GetXRef())) sfield := reflect.StructField{ Name: fieldName, Type: reflect.StructOf(subBuilder), Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, name)), } sfields = append(sfields, sfield) } for _, prop := range schema.GetProperties().GetAdditionalProperties() { fieldName := capitalize(prop.GetName()) if prop.GetValue().GetReference() != nil { subBuilder := buildDynamicStruct(doc, prop.GetValue()) sfield := reflect.StructField{ Name: fieldName, Type: reflect.StructOf(subBuilder), Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, prop.GetName())), } sfields = append(sfields, sfield) } if prop.GetValue().GetSchema() != nil { sfield := reflect.StructField{ Name: fieldName, Type: reflect.TypeOf(getGoType(doc, prop.GetValue())), Tag: reflect.StructTag(fmt.Sprintf(`json:"%s"`, prop.GetName())), } sfields = append(sfields, sfield) } } return sfields } func findReference(doc *openapi.Document, ref string) *openapi.SchemaOrReference { var result *openapi.SchemaOrReference ref = strings.Split(ref, "#/components/schemas/")[1] for _, prop := range doc.Components.Schemas.GetAdditionalProperties() { if prop.Name == ref { result = prop.Value } } return result } // Преобразование типа OpenAPI в тип Go func getGoType(doc *openapi.Document, schema *openapi.SchemaOrReference) interface{} { switch schema.GetSchema().Type { case "string": return "" case "integer": return 0 case "boolean": return false case "array": return []interface{}{} case "object": return buildDynamicStruct(doc, schema) default: return nil } } func capitalize(fieldName string) string { if fieldName == "" { return fieldName } // Заменяем точки на подчеркивания для унификации fieldName = strings.ReplaceAll(fieldName, ".", "_") // Разделяем строку по подчеркиваниям parts := strings.Split(fieldName, "_") if len(parts) == 1 { return strings.ToUpper(fieldName[:1]) + fieldName[1:] } // Обрабатываем каждый фрагмент for i := 0; i < len(parts); i++ { // Капитализируем первые буквы всех частей, кроме первой parts[i] = strings.Title(parts[i]) //cases.Title(language.English).String(parts[i]) } // Собираем строку обратно, соединяя части без подчеркиваний return strings.Join(parts, "") }