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 "" }