Skip to content
Snippets Groups Projects
Select Git revision
  • 42bbb1056e0e3e0394a6e168c4326d5643b9f924
  • main default protected
  • v1.298.0
  • v1.297.0
  • v1.296.0
  • v1.295.0
  • v1.294.0
  • v1.293.0
  • v1.292.0
  • v1.291.0
  • v1.290.0
  • v1.289.0
  • v1.288.0
  • v1.287.0
  • v1.286.0
  • v1.285.0
  • v1.284.0
  • v1.283.0
  • v1.282.0
  • v1.281.0
  • v1.280.0
  • v1.279.0
22 results

api_documentation.go

Blame
  • api_documentation.go 4.19 KiB
    package api_documentation
    
    import (
    	"fmt"
    	"reflect"
    	"strings"
    
    	"gitlab.com/uafrica/go-utils/handler_utils"
    
    	"gitlab.com/uafrica/go-utils/errors"
    )
    
    type NoParams struct{}
    
    type Docs struct {
    	Paths map[string]DocPath `json:"paths"`
    }
    
    type DocPath struct {
    	Methods map[string]DocMethod `json:"methods"`
    }
    
    type DocMethod struct {
    	Description string              `json:"description"`
    	Parameters  map[string]DocParam `json:"parameters,omitempty"`
    	Request     interface{}         `json:"request,omitempty"`
    	Response    interface{}         `json:"response,omitempty"`
    }
    
    type DocParam struct {
    	Name        string
    	Type        string
    	Description string
    }
    
    func GetDocs(endpointHandlers map[string]map[string]interface{}) (Docs, error) {
    	docs := Docs{
    		Paths: map[string]DocPath{},
    	}
    	for path, methods := range endpointHandlers {
    		docPath := DocPath{
    			Methods: map[string]DocMethod{},
    		}
    		for method, methodHandler := range methods {
    			docMethod := DocMethod{}
    			if handler, ok := methodHandler.(handler_utils.Handler); !ok {
    				docMethod.Description = "Not available"
    			} else {
    				//purpose
    				docMethod.Description = "Not available - see request and response structs"
    
    				//describe parameters
    				docMethod.Parameters = map[string]DocParam{}
    				for i := 0; i < handler.RequestParamsType.NumField(); i++ {
    					f := handler.RequestParamsType.Field(i)
    
    					name := f.Tag.Get("json")
    					if name == "" {
    						name = f.Name
    					}
    
    					docMethod.Parameters[f.Name] = DocParam{
    						Name:        name,
    						Type:        fmt.Sprintf("%v", f.Type),
    						Description: f.Tag.Get("doc"),
    					}
    				}
    
    				//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.Methods[method] = docMethod
    		}
    		docs.Paths[path] = docPath
    	}
    	return docs, nil
    }
    
    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
    }