Select Git revision
api_documentation.go
api_documentation.go 6.08 KiB
package api_documentation
import (
"fmt"
"reflect"
"strings"
"gitlab.com/uafrica/go-utils/handler_utils"
)
type NoParams struct{}
type DocPath map[string]DocMethodInfo
type Docs struct {
Paths map[string]DocPath `json:"paths"`
Definitions map[string]interface{}
}
type DocMethodInfo struct {
Summary string `json:"summary"`
Tags []string `json:"tags"`
Parameters []DocParam `json:"parameters,omitempty"`
Responses map[string]DocResponseValue `json:"responses,omitempty"`
}
type DocParam struct {
Name string
In string
Type string
Description string
Schema DocSchema
}
type DocSchema struct {
Ref string `json:"$ref"`
}
type DocResponseValue struct {
Description string `json:"description"`
Schema *DocSchemaResponse `json:"schema,omitempty"`
}
type DocSchemaResponse struct {
Type *string `json:"type,omitempty"`
Items DocSchema `json:"items"`
}
func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) {
docs := Docs{
Paths: map[string]DocPath{},
}
var validationError error
if endpointHandlers, validationError = handler_utils.ValidateAPIEndpoints(endpointHandlers); validationError != nil {
return Docs{}, validationError
}
for path, methods := range endpointHandlers {
docPath := DocPath{}
for method, methodHandler := range methods {
docMethod := DocMethodInfo{}
if handler, ok := methodHandler.(handler_utils.Handler); !ok {
docMethod.Summary = "Not available"
} else {
//purpose
docMethod.Summary = "Not available - see request and response structs"
docMethod.Tags = []string{path}
//describe parameters
docMethod.Parameters = []DocParam{}
for i := 0; i < handler.RequestParamsType.NumField(); i++ {
f := handler.RequestParamsType.Field(i)
name := f.Tag.Get("json")
if name == "" {
name = f.Name
}
parameter := DocParam{
Name: name,
Type: fmt.Sprintf("%v", f.Type),
Description: f.Tag.Get("doc"),
}
docMethod.Parameters = append(docMethod.Parameters, parameter)
}
// Request
if handler.RequestBodyType != nil {
body := reflect.New(handler.RequestBodyType).Interface()
bodyTypeString := getType(body)
parameter := DocParam{
Name: "body",
In: "body",
Schema: DocSchema{Ref: "#/definitions/"+bodyTypeString},
}
docMethod.Parameters = append(docMethod.Parameters, parameter)
docs.Definitions[bodyTypeString] = ""
}
// Response
if handler.ResponseType != nil {
responses := map[string]DocResponseValue{}
responseBody := reflect.New(handler.RequestBodyType).Interface()
responseBodyTypeString := getType(responseBody)
responses["200"] = DocResponseValue{
Description: "successful operation",
Schema: &DocSchemaResponse{
Items: DocSchema{Ref: "#/definitions/"+responseBodyTypeString},
},
}
docMethod.Responses = responses
docs.Definitions[responseBodyTypeString] = ""
}
//describe request schema
// var err error
// docMethod.Request, err = DocSchema(fmt.Sprintf("%s %s %s", method, path, "request"), handler.RequestBodyType)
// if err != nil {
// return Docs{}, errors.Wrapf(err, "failed to document request")
// }
// docMethod.Response, err = DocSchema(fmt.Sprintf("%s %s %s", method, path, "response"), handler.ResponseType)
// if err != nil {
// return Docs{}, errors.Wrapf(err, "failed to document response")
// }
}
docPath[strings.ToLower(method)] = docMethod
}
docs.Paths[path] = docPath
}
return docs, nil
}
func getType(myvar interface{}) string {
if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr {
return t.Elem().Name()
} else {
return t.Name()
}
}
//
// func DocSchema(description string, t reflect.Type) (interface{}, error) {
// if t == nil {
// return nil, nil
// }
// schema := map[string]interface{}{
// "description": description,
// }
//
// 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,
// reflect.Float64, reflect.Float32,
// reflect.Bool,
// reflect.String:
// schema["type"] = fmt.Sprintf("%v", t)
//
// case reflect.Interface:
// schema["type"] = "interface{}" //any value...?
//
// case reflect.Struct:
// schema["type"] = "object"
// properties := map[string]interface{}{}
// for i := 0; i < t.NumField(); i++ {
// f := t.Field(i)
// if !f.Anonymous {
// fieldName := f.Tag.Get("json")
// if fieldName == "" {
// fieldName = f.Name
// }
// if fieldName == "-" {
// continue //json does not marshal these
// }
// fieldName = strings.Replace(fieldName, ",omitempty", "", -1)
//
// var err error
// fieldDesc := f.Tag.Get("doc")
// if fieldDesc == "" {
// fieldDesc = description + "." + fieldName
// }
// properties[fieldName], err = DocSchema(fieldDesc, 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 {
// // 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())
// // 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
// }