diff --git a/api_documentation/api_documentation.go b/api_documentation/api_documentation.go index 13810e02d49404069c6c3e92599e21deaf5e6fdf..25257fbe19d911f90e009b4509971c68509da7d3 100644 --- a/api_documentation/api_documentation.go +++ b/api_documentation/api_documentation.go @@ -5,6 +5,7 @@ import ( "reflect" "strings" + "gitlab.com/uafrica/go-utils/errors" "gitlab.com/uafrica/go-utils/handler_utils" ) @@ -14,23 +15,34 @@ type DocPath map[string]DocMethodInfo type Docs struct { 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 { - Summary string `json:"summary"` + Description string `json:"description"` Tags []string `json:"tags"` + RequestBody DocRequestBody `json:"requestBody"` Parameters []DocParam `json:"parameters,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 { - Name string - In string - Type string - Description string - Schema DocSchema + Name string `json:"name"` + In string `json:"in"` + Type string `json:"type"` + Description string `json:"description"` + Schema DocSchema `json:"schema"` } type DocSchema struct { @@ -38,8 +50,8 @@ type DocSchema struct { } type DocResponseValue struct { - Description string `json:"description"` - Schema *DocSchemaResponse `json:"schema,omitempty"` + Description string `json:"description"` + Content map[string]interface{} `json:"content"` } type DocSchemaResponse struct { @@ -51,6 +63,7 @@ type DocSchemaResponse struct { func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { docs := Docs{ Paths: map[string]DocPath{}, + Components: DocSchemas{Schemas: map[string]interface{}{}}, } var validationError error @@ -64,10 +77,10 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { for method, methodHandler := range methods { docMethod := DocMethodInfo{} if handler, ok := methodHandler.(handler_utils.Handler); !ok { - docMethod.Summary = "Not available" + docMethod.Description = "Not available" } else { //purpose - docMethod.Summary = "Not available - see request and response structs" + docMethod.Description = "Not available - see request and response structs" docMethod.Tags = []string{path} //describe parameters @@ -93,31 +106,47 @@ func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) { 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.RequestBody = DocRequestBody{ + 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) - docs.Definitions[bodyTypeString] = "" + schema, err := StructSchema(handler.RequestBodyType.Field(0).Type) + if err != nil { + return Docs{}, err + } + docs.Components.Schemas[bodyTypeString] = schema } // Response if handler.ResponseType != nil { responses := map[string]DocResponseValue{} - responseBody := reflect.New(handler.RequestBodyType).Interface() + responseBody := reflect.New(handler.ResponseType).Interface() responseBodyTypeString := getType(responseBody) responses["200"] = DocResponseValue{ - Description: "successful operation", - Schema: &DocSchemaResponse{ - Items: DocSchema{Ref: "#/definitions/"+responseBodyTypeString}, + Description: "Some description", + Content: map[string]interface{} { + "application/json": map[string]interface{}{ + "schema": DocSchema{Ref: "#/components/schemas/"+responseBodyTypeString}, + }, }, } 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 { 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 -// } + +func StructSchema(t reflect.Type) (interface{}, error) { + if t == nil { + return nil, nil + } + schema := map[string]interface{}{} + + + 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 = 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 { + // 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 +}