opier/encode/func.go

282 lines
8.1 KiB
Go

package encode // import "udico.de/uditaren/opier/encode"
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"udico.de/opier/openapi"
)
type simpleparam struct {
Name string
Type string
In string
}
// implement sort interface on simpleparam
type simpleparamslice []*simpleparam
func (s simpleparamslice) Len() int { return len(s) }
func (s simpleparamslice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s simpleparamslice) Less(i, j int) bool {
val := func(s string) int {
switch s {
case "path":
return 0
case "header":
return 1
case "query":
return 2
}
return 99
}
iVal := val(s[i].In)
jVal := val(s[j].In)
iStr := s[i].Name
jStr := s[j].Name
if iVal == jVal {
return strings.Compare(iStr, jStr) < 0
}
return iVal < jVal
}
// Funcs generates all functions for a given tag
//
// tag is a
// tag name
func (e Encoder) Funcs(tag string, operations []*openapi.Operation) string {
tBuf := bytes.Buffer{}
tBuf.WriteString(fmt.Sprintf("package %v\n\n", e.Package))
tBuf.WriteString(e.GeneratedHeader())
tBuf.WriteString(e.tagType(tag))
tFuncPrefix := "p" + NormalizeName(tag)
for _, op := range operations {
// name -> type
tParams := make([]*simpleparam, 0, 5)
if op.Description != "" {
tDesc := strings.Split(strings.TrimSpace(op.Description), "\n")
for _, tLine := range tDesc {
tBuf.WriteString(fmt.Sprintf("// %v\n", tLine))
}
}
for _, params := range op.Path().Parameters {
tBuf.WriteString("// param [PATH] ")
if params.Ref != "" {
// ref param
tRefParam := e.api.ResolveParameter(params.Ref)
tBuf.WriteString(fmt.Sprintf("REF: %v\n", params.Ref))
tBuf.WriteString(Comment(tRefParam.Name, 2))
tParams = append(tParams, &simpleparam{
Name: tRefParam.Name,
Type: tRefParam.Schema.GoType(),
In: "path",
})
} else {
tBuf.WriteString(fmt.Sprintf("TYPE: %v\n", params.Schema.Type))
tParams = append(tParams, &simpleparam{
Name: params.Name,
Type: params.Schema.GoType(),
In: "path",
})
}
}
for _, params := range op.Parameters {
//tBuf.WriteString("// param ")
tBuf.WriteString(Comment(fmt.Sprintf("param [%v] %v: %v\n", params.In, params.Name, params.Description), 0))
tParams = append(tParams, &simpleparam{
Name: params.Name,
Type: params.Schema.GoType(),
In: string(params.In),
})
}
if op.RequestBody != nil {
tBuf.WriteString(fmt.Sprintf("// body: %v\n", op.RequestBody.Description))
for k, v := range op.RequestBody.Content {
tBuf.WriteString(fmt.Sprintf("// [%v]: %v\n", k, v.Schema.Ref))
}
}
var tResponseType *openapi.ResponseObject = nil
for tCode, tResponse := range op.Responses {
tBuf.WriteString(Comment(fmt.Sprintf("response [%v]: %v\n", tCode, tResponse.Description), 0))
tCodeVal, _ := strconv.Atoi(tCode)
if tCodeVal/100 == 2 {
tResponseType = tResponse
}
}
tResponses := make([]*simpleparam, 0, 2)
if tResponseType != nil {
for k, v := range tResponseType.Headers {
// each given responseheader is a return parameter
tResponses = append(tResponses, &simpleparam{
Name: k,
Type: v.Schema.TypeOrReference(),
In: "header",
})
}
if len(tResponseType.Content) > 0 {
tSchema := tResponseType.Content["application/json"].Schema
tResponses = append(tResponses, &simpleparam{
Name: "ret",
Type: tSchema.GoType(),
In: "body",
})
}
}
sort.Sort(simpleparamslice(tParams))
sort.Sort(simpleparamslice(tResponses))
// parameter list:
tResponses = append(tResponses, &simpleparam{
Name: "err",
Type: "error",
})
tParamBuf := &strings.Builder{}
for i, tParam := range tParams {
if i > 0 {
tParamBuf.WriteString(", ")
}
tParamBuf.WriteString(NormalizeName(tParam.Name))
tParamBuf.WriteString(" " + tParam.Type)
}
// body parameter:
if op.RequestBody != nil {
ref := op.RequestBody.Content["application/json"].Schema.Ref
if ref != "" {
if tParamBuf.Len() > 0 {
tParamBuf.WriteString(", ")
}
tParamBuf.WriteString(fmt.Sprintf("data *%v", e.api.ResolveSchema(ref).Name()))
} else {
tParamBuf.WriteString(" /* IMPLEMENT BODY SCHEMA which is not $ref */")
}
}
tParamStr := tParamBuf.String()
tResponseBuf := &strings.Builder{}
tResponseBuf.WriteString("(")
for i, tResponse := range tResponses {
if i > 0 {
tResponseBuf.WriteString(", ")
}
tResponseBuf.WriteString(tResponse.Name + " ")
tResponseBuf.WriteString(tResponse.Type)
}
tResponseBuf.WriteString(")")
tResponseStr := tResponseBuf.String()
tBuf.WriteString(fmt.Sprintf("func (p %v) %v(%v) %v {\n", tFuncPrefix, NormalizeName(op.OperationId), tParamStr, tResponseStr))
tBuf.WriteString(fmt.Sprintf(" // [%v]%v operation here \n", op.Method(), op.Path().Name))
// assemble request
// insert path parameters
tBuf.WriteString(fmt.Sprintf(" tPath := p.base+\"%v\"\n", op.Path().Name))
for _, p := range tParams {
if p.In == "path" {
tBuf.WriteString(fmt.Sprintf(" replacePathParam(&tPath, \"%v\", %v)\n", p.Name, NormalizeName(p.Name)))
}
}
// post/put params ("body")
tBufBody := "nil"
if op.RequestBody != nil {
tBuf.WriteString(" var tData []byte = nil\n")
tBuf.WriteString(" tData, err = json.Marshal(data)\n")
tBuf.WriteString(" if err != nil {\n")
tBuf.WriteString(" return\n")
tBuf.WriteString(" }\n")
tBuf.WriteString(" tBuf := bytes.NewBuffer(tData)\n")
tBufBody = "tBuf"
}
tBuf.WriteString(fmt.Sprintf(" req, _ := http.NewRequest(\"%v\", tPath, %v)\n", op.Method(), tBufBody))
// add header parameters
for _, p := range tParams {
if p.In == "header" {
tBuf.WriteString(fmt.Sprintf(" req.Header.Add(\"%v\", \"%v\")\n", p.Name, NormalizeName(p.Name)))
}
}
// add query parameters
tQBuf := &strings.Builder{}
tQBufHasData := false
tQBuf.WriteString(" q := req.URL.Query()\n")
for _, p := range tParams {
if p.In == "query" {
tQBuf.WriteString(fmt.Sprintf(" addQueryParameter(&q, \"%v\", joinIfArray(%v))\n", p.Name, NormalizeName(p.Name)))
tQBufHasData = true
}
}
tQBuf.WriteString(" req.URL.RawQuery = q.Encode()\n\n")
if tQBufHasData {
tBuf.WriteString(tQBuf.String())
}
tBuf.WriteString(" resp, err := p.client.Do(req)\n")
tBuf.WriteString(" if err != nil {\n return\n }\n")
tBuf.WriteString(" defer resp.Body.Close()\n")
tBuf.WriteString(" if resp.StatusCode / 100 != 2 {\n err = errors.New(resp.Status)\n return\n }\n")
// parse response
for _, r := range tResponses {
if r.In == "header" {
tBuf.WriteString(fmt.Sprintf(" %v = resp.Header.Get(\"%v\")\n", NormalizeName(r.Name), r.Name))
}
}
for _, r := range tResponses {
if r.In == "body" {
tType := r.Type
if strings.HasPrefix(tType, "*") {
tType = "&" + strings.TrimPrefix(tType, "*")
}
tBuf.WriteString(" tResponseData, err := ioutil.ReadAll(resp.Body)\n")
tBuf.WriteString(" if err != nil {\n")
tBuf.WriteString(" return\n")
tBuf.WriteString(" }\n")
tBuf.WriteString(fmt.Sprintf(" ret = %v{}\n", tType))
tBuf.WriteString(" err = json.Unmarshal(tResponseData, ret)\n")
break // there can be only one
}
}
tBuf.WriteString(fmt.Sprintf(" return\n}\n\n"))
}
return tBuf.String()
}
func (e Encoder) tagType(tag string) string {
tNorm := NormalizeName(tag)
tBuf := bytes.Buffer{}
tBuf.WriteString("import (\n \"bytes\"\n \"encoding/json\"\n \"errors\"\n \"io/ioutil\"\n \"net/http\"\n)\n")
tBuf.WriteString(Comment(e.getTagDescription(tag), 0))
tBuf.WriteString(fmt.Sprintf("type p%v struct {\n", tNorm))
tBuf.WriteString(" client *http.Client\n")
tBuf.WriteString(" base string\n")
tBuf.WriteString("}\n\n")
tBuf.WriteString(fmt.Sprintf("func %v(aClient *http.Client, aBase string) p%v {\n", NormalizeName("new-"+tag), tNorm))
tBuf.WriteString(fmt.Sprintf(" return p%v{\n", tNorm))
tBuf.WriteString(" client: aClient,\n")
tBuf.WriteString(" base: aBase,\n")
tBuf.WriteString(" }\n")
tBuf.WriteString("}\n\n")
return tBuf.String()
}
func (e Encoder) getTagDescription(tag string) string {
for _, v := range e.api.Tags {
if v.Name == tag {
return v.Description
}
}
return ""
}