Skip to content
Snippets Groups Projects
Commit e6979b5c authored by Johan de Klerk's avatar Johan de Klerk
Browse files

docs: Better open api output

parent 515ab5b3
Branches
Tags
No related merge requests found
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"gitlab.com/uafrica/go-utils/errors"
"gitlab.com/uafrica/go-utils/handler_utils" "gitlab.com/uafrica/go-utils/handler_utils"
) )
...@@ -14,23 +15,34 @@ type DocPath map[string]DocMethodInfo ...@@ -14,23 +15,34 @@ type DocPath map[string]DocMethodInfo
type Docs struct { type Docs struct {
Paths map[string]DocPath `json:"paths"` Paths map[string]DocPath `json:"paths"`
Definitions map[string]interface{} Components DocSchemas `json:"components"`
} }
type DocSchemas struct {
Schemas map[string]interface{} `json:"schemas"`
}
type DocMethodInfo struct { type DocMethodInfo struct {
Summary string `json:"summary"` Description string `json:"description"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
RequestBody DocRequestBody `json:"requestBody"`
Parameters []DocParam `json:"parameters,omitempty"` Parameters []DocParam `json:"parameters,omitempty"`
Responses map[string]DocResponseValue `json:"responses,omitempty"` Responses map[string]DocResponseValue `json:"responses,omitempty"`
} }
type DocRequestBody struct {
Description string `json:"description"`
Required bool `json:"required"`
Content map[string]interface{} `json:"content"`
}
type DocParam struct { type DocParam struct {
Name string Name string `json:"name"`
In string In string `json:"in"`
Type string Type string `json:"type"`
Description string Description string `json:"description"`
Schema DocSchema Schema DocSchema `json:"schema"`
} }
type DocSchema struct { type DocSchema struct {
...@@ -39,7 +51,7 @@ type DocSchema struct { ...@@ -39,7 +51,7 @@ type DocSchema struct {
type DocResponseValue struct { type DocResponseValue struct {
Description string `json:"description"` Description string `json:"description"`
Schema *DocSchemaResponse `json:"schema,omitempty"` Content map[string]interface{} `json:"content"`
} }
type DocSchemaResponse struct { type DocSchemaResponse struct {
...@@ -51,6 +63,7 @@ type DocSchemaResponse struct { ...@@ -51,6 +63,7 @@ type DocSchemaResponse struct {
func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) {
docs := Docs{ docs := Docs{
Paths: map[string]DocPath{}, Paths: map[string]DocPath{},
Components: DocSchemas{Schemas: map[string]interface{}{}},
} }
var validationError error var validationError error
...@@ -64,10 +77,10 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { ...@@ -64,10 +77,10 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) {
for method, methodHandler := range methods { for method, methodHandler := range methods {
docMethod := DocMethodInfo{} docMethod := DocMethodInfo{}
if handler, ok := methodHandler.(handler_utils.Handler); !ok { if handler, ok := methodHandler.(handler_utils.Handler); !ok {
docMethod.Summary = "Not available" docMethod.Description = "Not available"
} else { } else {
//purpose //purpose
docMethod.Summary = "Not available - see request and response structs" docMethod.Description = "Not available - see request and response structs"
docMethod.Tags = []string{path} docMethod.Tags = []string{path}
//describe parameters //describe parameters
...@@ -93,31 +106,47 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { ...@@ -93,31 +106,47 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) {
if handler.RequestBodyType != nil { if handler.RequestBodyType != nil {
body := reflect.New(handler.RequestBodyType).Interface() body := reflect.New(handler.RequestBodyType).Interface()
bodyTypeString := getType(body) bodyTypeString := getType(body)
parameter := DocParam{
Name: "body",
In: "body", docMethod.RequestBody = DocRequestBody{
Schema: DocSchema{Ref: "#/definitions/"+bodyTypeString}, Description: "Some description",
Required: true,
Content: map[string]interface{} {
"application/json": map[string]interface{}{
"schema": DocSchema{Ref: "#/components/schemas/"+bodyTypeString},
},
},
} }
docMethod.Parameters = append(docMethod.Parameters, parameter) schema, err := StructSchema(handler.RequestBodyType.Field(0).Type)
docs.Definitions[bodyTypeString] = "" if err != nil {
return Docs{}, err
}
docs.Components.Schemas[bodyTypeString] = schema
} }
// Response // Response
if handler.ResponseType != nil { if handler.ResponseType != nil {
responses := map[string]DocResponseValue{} responses := map[string]DocResponseValue{}
responseBody := reflect.New(handler.RequestBodyType).Interface() responseBody := reflect.New(handler.ResponseType).Interface()
responseBodyTypeString := getType(responseBody) responseBodyTypeString := getType(responseBody)
responses["200"] = DocResponseValue{ responses["200"] = DocResponseValue{
Description: "successful operation", Description: "Some description",
Schema: &DocSchemaResponse{ Content: map[string]interface{} {
Items: DocSchema{Ref: "#/definitions/"+responseBodyTypeString}, "application/json": map[string]interface{}{
"schema": DocSchema{Ref: "#/components/schemas/"+responseBodyTypeString},
},
}, },
} }
docMethod.Responses = responses docMethod.Responses = responses
docs.Definitions[responseBodyTypeString] = ""
schema, err := StructSchema(handler.ResponseType.Field(0).Type)
if err != nil {
return Docs{}, err
}
docs.Components.Schemas[responseBodyTypeString] = schema
} }
...@@ -152,83 +181,86 @@ func getType(myvar interface{}) string { ...@@ -152,83 +181,86 @@ func getType(myvar interface{}) string {
return t.Name() return t.Name()
} }
} }
//
// func DocSchema(description string, t reflect.Type) (interface{}, error) { func StructSchema(t reflect.Type) (interface{}, error) {
// if t == nil { if t == nil {
// return nil, nil return nil, nil
// } }
// schema := map[string]interface{}{ schema := map[string]interface{}{}
// "description": description,
// }
// description := ""
// if t.Kind() == reflect.Ptr {
// schema["optional"] = true
// t = t.Elem() if t.Kind() == reflect.Ptr {
// } schema["optional"] = true
// t = t.Elem()
// switch t.Kind() { }
// case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int,
// reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint, switch t.Kind() {
// reflect.Float64, reflect.Float32, case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int,
// reflect.Bool, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint,
// reflect.String: reflect.Float64, reflect.Float32,
// schema["type"] = fmt.Sprintf("%v", t) reflect.Bool,
// reflect.String:
// case reflect.Interface: schema["type"] = fmt.Sprintf("%v", t)
// schema["type"] = "interface{}" //any value...?
// case reflect.Interface:
// case reflect.Struct: schema["type"] = "interface{}" //any value...?
// schema["type"] = "object"
// properties := map[string]interface{}{} case reflect.Struct:
// for i := 0; i < t.NumField(); i++ { schema["type"] = "object"
// f := t.Field(i) properties := map[string]interface{}{}
// if !f.Anonymous {
// fieldName := f.Tag.Get("json") for i := 0; i < t.NumField(); i++ {
// if fieldName == "" { f := t.Field(i)
// fieldName = f.Name if !f.Anonymous {
// } fieldName := f.Tag.Get("json")
// if fieldName == "-" { if fieldName == "" {
// continue //json does not marshal these fieldName = f.Name
// } }
// fieldName = strings.Replace(fieldName, ",omitempty", "", -1) if fieldName == "-" {
// continue //json does not marshal these
// var err error }
// fieldDesc := f.Tag.Get("doc") fieldName = strings.Replace(fieldName, ",omitempty", "", -1)
// if fieldDesc == "" {
// fieldDesc = description + "." + fieldName var err error
// } fieldDesc := f.Tag.Get("doc")
// properties[fieldName], err = DocSchema(fieldDesc, f.Type) if fieldDesc == "" {
fieldDesc = description + "." + fieldName
}
properties[fieldName], err = StructSchema(f.Type)
if err != nil {
return nil, errors.Wrapf(err, "failed to document %v.%s", t, fieldName)
}
}
}
schema["properties"] = properties
case reflect.Map:
schema["type"] = "map"
// keySchema, err := DocSchema("key", t.Key())
// if err != nil { // if err != nil {
// return nil, errors.Wrapf(err, "failed to document %v.%s", t, fieldName) // return nil, errors.Wrapf(err, "cannot make schema for %v map key", t)
// } // }
// } // schema["key"] = keySchema
// }
// schema["properties"] = properties
//
// case reflect.Map:
// schema["type"] = "map"
// // keySchema, err := DocSchema("key", t.Key())
// // if err != nil {
// // return nil, errors.Wrapf(err, "cannot make schema for %v map key", t)
// // }
// // schema["key"] = keySchema
// // // elemSchema, err := DocSchema("items", t.Elem())
// // if err != nil {
// // return nil, errors.Wrapf(err, "cannot make schema for %v map elem", t)
// // }
// // schema["items"] = elemSchema
//
// case reflect.Slice:
// schema["type"] = "array"
// // elemSchema, err := DocSchema("items", t.Elem()) // // elemSchema, err := DocSchema("items", t.Elem())
// // if err != nil { // if err != nil {
// // return nil, errors.Wrapf(err, "cannot make schema for %v slice elem", t) // return nil, errors.Wrapf(err, "cannot make schema for %v map elem", t)
// // }
// // schema["items"] = elemSchema
//
// default:
// return nil, errors.Errorf("cannot generate schema for %v kind=%v", t, t.Kind())
// } // }
// // schema["items"] = elemSchema
// return schema, nil
case reflect.Slice:
schema["type"] = "array"
// elemSchema, err := DocSchema("items", t.Elem())
// if err != nil {
// return nil, errors.Wrapf(err, "cannot make schema for %v slice elem", t)
// } // }
// schema["items"] = elemSchema
default:
return nil, errors.Errorf("cannot generate schema for %v kind=%v", t, t.Kind())
}
return schema, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment