func addSwaggerResponse(responses map[string]*SwaggerResponse, errType string, sym string, errComment string) { code := rdl.StatusCode(sym) var schema *SwaggerType if sym != "NO_CONTENT" { schema = new(SwaggerType) schema.Ref = "#/definitions/" + errType } description := rdl.StatusMessage(sym) if errComment != "" { description += " - " + errComment } responses[code] = &SwaggerResponse{description, schema} }
func goMethodBody(reg rdl.TypeRegistry, r *rdl.Resource, precise bool) string { errorReturn := "return nil, err" dataReturn := "return data, nil" noContent := r.Expected == "NO_CONTENT" && r.Alternatives == nil if noContent { errorReturn = "return err" dataReturn = "return nil" } if r.Outputs != nil { dret := "return data" eret := "return nil" for _, o := range r.Outputs { dret += ", " + goName(string(o.Name)) eret += ", \"\"" } dret += ", nil" eret += ", err" dataReturn = dret errorReturn = eret } headers := map[string]rdl.Identifier{} for _, in := range r.Inputs { if in.Header != "" { headers[in.Header] = in.Name } } s := "" httpArg := "url, nil" if len(headers) > 0 { //not optimal: when the headers are empty ("") they are still included httpArg = "url, headers" s += "\theaders := map[string]string{\n" for k, v := range headers { s += fmt.Sprintf("\t\t%q: %s,\n", k, v) } s += "\t}\n" } url := explodeURL(reg, r) s += "\turl := client.URL + " + url + "\n" method := capitalize(strings.ToLower(r.Method)) assign := ":=" switch method { case "Get", "Delete": s += "\tresp, err := client.http" + method + "(" + httpArg + ")\n" case "Put", "Post", "Patch": bodyParam := "?" for _, in := range r.Inputs { name := in.Name if !in.PathParam && in.QueryParam == "" && in.Header == "" { bodyParam = string(name) break } } s += "\tcontentBytes, err := json.Marshal(" + bodyParam + ")\n" s += "\tif err != nil {\n\t\t" + errorReturn + "\n\t}\n" s += "\tresp, err := client.http" + method + "(" + httpArg + ", contentBytes)\n" assign = "=" case "Options": bodyParam := "?" for _, in := range r.Inputs { name := in.Name if !in.PathParam && in.QueryParam == "" && in.Header == "" { bodyParam = string(name) break } } if bodyParam != "?" { s += "\tcontentBytes, err := json.Marshal(" + bodyParam + ")\n" s += "\tif err != nil {\n\t\t" + errorReturn + "\n\t}\n" s += "\tresp, err := client.http" + method + "(" + httpArg + ", contentBytes)\n" assign = "=" } else { s += "\tresp, err := client.http" + method + "(" + httpArg + ", nil)\n" } } s += "\tif err != nil {\n\t\t" + errorReturn + "\n\t}\n" s += "\tcontentBytes, err " + assign + " ioutil.ReadAll(resp.Body)\n" s += "\tresp.Body.Close()\n" s += "\tif err != nil {\n\t\t" + errorReturn + "\n\t}\n" s += "\tswitch resp.StatusCode {\n" //loop for all expected results var expected []string expected = append(expected, rdl.StatusCode(r.Expected)) couldBeNoContent := "NO_CONTENT" == r.Expected couldBeNotModified := "NOT_MODIFIED" == r.Expected for _, e := range r.Alternatives { if "NO_CONTENT" == e { couldBeNoContent = true } if "NOT_MODIFIED" == e { couldBeNotModified = true } expected = append(expected, rdl.StatusCode(e)) } s += "\tcase " + strings.Join(expected, ", ") + ":\n" if couldBeNoContent || couldBeNotModified { if !noContent { s += "\t\tvar data *" + string(r.Type) + "\n" tmp := "" if couldBeNoContent { tmp = "204 != resp.StatusCode" } if couldBeNotModified { if tmp != "" { tmp += " || " } tmp += "304 != resp.StatusCode" } s += "\t\tif " + tmp + " {\n" s += "\t\t\terr = json.Unmarshal(contentBytes, &data)\n" s += "\t\t\tif err != nil {\n\t\t\t\t" + errorReturn + "\n\t\t\t}\n" s += "\t\t}\n" } } else { s += "\t\tvar data *" + string(r.Type) + "\n" s += "\t\terr = json.Unmarshal(contentBytes, &data)\n" s += "\t\tif err != nil {\n\t\t\t" + errorReturn + "\n\t\t}\n" } //here, define the output headers if r.Outputs != nil { for _, o := range r.Outputs { otype := goType(reg, o.Type, false, "", "", precise, true) header := fmt.Sprintf("resp.Header.Get(rdl.FoldHttpHeaderName(%q))", o.Header) if otype != "string" { header = otype + "(" + header + ")" } s += "\t\t" + goName(string(o.Name)) + " := " + header + "\n" } } s += "\t\t" + dataReturn + "\n" //end loop s += "\tdefault:\n" s += "\t\tvar errobj rdl.ResourceError\n" s += "\t\tjson.Unmarshal(contentBytes, &errobj)\n" s += "\t\tif errobj.Code == 0 {\n" s += "\t\t\terrobj.Code = resp.StatusCode\n" s += "\t\t}\n" s += "\t\tif errobj.Message == \"\" {\n" s += "\t\t\terrobj.Message = string(contentBytes)\n" s += "\t\t}\n" s += "\t\t" + errorReturn + "obj\n" s += "\t}" return s }
func goHandlerBody(reg rdl.TypeRegistry, name string, r *rdl.Resource, precise bool, prefixEnums bool) string { s := "" var fargs []string bodyName := "" for _, in := range r.Inputs { name := "arg" + capitalize(string(in.Name)) if in.QueryParam != "" { qname := in.QueryParam if in.Optional || in.Default != nil { s += goParamInit(reg, qname, name, in.Type, in.Default, in.Optional, precise, prefixEnums) } else { log.Println("RDL error: queryparam must either be optional or have a default value") } fargs = append(fargs, name) } else if in.PathParam { bt := reg.BaseTypeName(in.Type) switch bt { case "Enum": s += fmt.Sprintf("\t%s := New%s(context.Params[%q])\n", name, in.Type, in.Name) case "Int32", "Int64", "Int16", "Int8": if precise { s += fmt.Sprintf("\t%s := %s(intFromString(context.Params[%q]))\n", name, in.Type, in.Name) } else { s += fmt.Sprintf("\t%s := intFromString(context.Params[%q])\n", name, in.Name) } case "Float32", "Float64": if precise { s += fmt.Sprintf("\t%s := %s(floatFromString(context.Params[%q]))\n", name, in.Type, in.Name) } else { s += fmt.Sprintf("\t%s := floatFromString(context.Params[%q])\n", name, in.Name) } default: if precise && strings.ToLower(string(in.Type)) != "string" { s += fmt.Sprintf("\t%s := %s(context.Params[%q])\n", name, in.Type, in.Name) } else { s += fmt.Sprintf("\t%s := context.Params[%q]\n", name, in.Name) } } fargs = append(fargs, name) } else if in.Header != "" { hname := in.Header def := "" if in.Default != nil { switch v := in.Default.(type) { case string: def = fmt.Sprintf("%q", v) default: panic(fmt.Sprintf("implement me, default value: %v", in)) } s += "\t" + name + "Optional := " + def + "\n" s += fmt.Sprintf("\t%s := rdl.HeaderParam(request, %q, %sOptional)\n", name, hname, name) } else if in.Optional { s += fmt.Sprintf("\t%s := rdl.OptionalHeaderParam(request, %q)\n", name, hname) } else { s += fmt.Sprintf("\t%s := rdl.HeaderParam(request, %q, \"\")\n", name, hname) } fargs = append(fargs, name) } else { bodyName = name s += "\tbody, oserr := ioutil.ReadAll(request.Body)\n" s += "\tif oserr != nil {\n" s += "\t\trdl.JSONResponse(writer, http.StatusBadRequest, rdl.ResourceError{Code: http.StatusBadRequest, Message: \"Bad request: \" + oserr.Error()})\n" s += "\t\treturn\n" s += "\t}\n" pgtype := goType(reg, in.Type, false, "", "", precise, true) s += "\tvar " + bodyName + " " + pgtype + "\n" s += "\toserr = json.Unmarshal(body, &" + bodyName + ")\n" s += "\tif oserr != nil {\n" s += "\t\trdl.JSONResponse(writer, http.StatusBadRequest, rdl.ResourceError{Code: http.StatusBadRequest, Message: \"Bad request: \" + oserr.Error()})\n" s += "\t\treturn\n" s += "\t}\n" fargs = append(fargs, bodyName) } } if r.Auth != nil { if r.Auth.Authenticate { s += authenticateTemplate } else if r.Auth.Action != "" && r.Auth.Resource != "" { resource := r.Auth.Resource i := strings.Index(resource, "{") for i >= 0 { j := strings.Index(resource[i:], "}") if j < 0 { break } j += i val := "string(arg" + capitalize(resource[i+1:j]) + ")" resource = resource[0:i] + "\" + " + val + " + \"" + resource[j+1:] i = strings.Index(resource, "{") } resource = "\"" + resource if strings.HasSuffix(resource, "+ \"") { resource = resource[0 : len(resource)-3] } else { resource = resource + "\"" } if strings.HasPrefix(resource, "\"\" + ") { resource = resource[5:] } s += fmt.Sprintf(authorizeTemplate, r.Auth.Action, resource) } else { log.Println("*** Badly formed auth spec in resource input:", r) } } methName, _ := goMethodName(reg, r, precise) sargs := "" if len(fargs) > 0 { sargs = ", " + strings.Join(fargs, ", ") } outHeaders := "" for _, v := range r.Outputs { outHeaders += ", " + string(v.Name) } noContent := r.Expected == "NO_CONTENT" && len(r.Alternatives) == 0 if noContent { s += "\terr" + outHeaders + " := adaptor.impl." + capitalize(methName) + "(context" + sargs + ")\n" } else { s += "\tdata" + outHeaders + ", err := adaptor.impl." + capitalize(methName) + "(context" + sargs + ")\n" } s += "\tif err != nil {\n" s += "\t\tswitch e := err.(type) {\n" s += "\t\tcase *rdl.ResourceError:\n" //special case the 304 response, which MUST have an etag in it for _, v := range r.Outputs { if strings.ToLower(v.Header) == "etag" { s += "\t\t\tif e.Code == 304 && " + string(v.Name) + " != \"\" {\n" s += "\t\t\t\twriter.Header().Set(\"" + v.Header + "\", " + string(v.Name) + ")\n" s += "\t\t\t}\n" break } } s += "\t\t\trdl.JSONResponse(writer, e.Code, err)\n" s += "\t\tdefault:\n" s += "\t\t\trdl.JSONResponse(writer, 500, &rdl.ResourceError{Code: 500, Message: e.Error()})\n" s += "\t\t}\n" s += "\t} else {\n" for _, v := range r.Outputs { vname := string(v.Name) if v.Optional { s += "\t\tif " + vname + " != nil {\n" s += "\t\t\twriter.Header().Set(\"" + v.Header + "\", " + vname + ")\n" s += "\t\t}\n" } else { s += "\t\twriter.Header().Set(\"" + v.Header + "\", " + vname + ")\n" } } if noContent { //other non-content responses? s += fmt.Sprintf("\t\twriter.WriteHeader(204)\n") } else { //fixme: handle alternative responses. How deos the handler pass them back? s += fmt.Sprintf("\t\trdl.JSONResponse(writer, %s, data)\n", rdl.StatusCode(r.Expected)) } s += "\t}\n" return s }
func formatResource(out io.Writer, registry rdl.TypeRegistry, rez *rdl.Resource) { fmt.Fprintf(out, "\n#### %s %s\n", strings.ToUpper(rez.Method), rez.Path) if rez.Comment != "" { fmt.Fprintf(out, "%s", formatBlock(rez.Comment, 0, 80, "")) } if len(rez.Inputs) > 0 { var rows [][]string for _, f := range rez.Inputs { fn := string(f.Name) ft := annotate(registry, f.Type) fs := "" if f.PathParam { fs = "path" } else if f.QueryParam != "" { fs = "query: " + f.QueryParam } else if f.Header != "" { fs = "header: " + f.Header // } else if f.Context != "" { // fs = "context: " + f.Context } else { fs = "body" } fo := "" if f.Optional { fo = "optional" } if f.Default != nil { s := optionalAnyToString(f.Default) if fo == "" { fo = "default=" + s } else { fo += ", default=" + s } } if f.Pattern != "" { if fo != "" { fo += ", " } fo += "pattern: " + f.Pattern } if f.Flag { if fo != "" { fo += ", " } fo += "flag" } fc := "" if f.Comment != "" { fc += f.Comment } row := []string{fn, ft, fs, fo, fc} rows = append(rows, row) } if rows != nil { fmt.Fprintf(out, "\n#### Request parameters:\n\n") formatTable(out, []string{"Name", "Type", "Source", "Options", "Description"}, rows) } } if len(rez.Outputs) > 0 { var rows [][]string for _, f := range rez.Outputs { fn := string(f.Name) ft := annotate(registry, f.Type) fd := "header: " + f.Header fo := "false" if f.Optional { fo = "true" } fc := "" if f.Comment != "" { fc = f.Comment } row := []string{fn, ft, fd, fo, fc} rows = append(rows, row) } if rows != nil { fmt.Fprintf(out, "\n#### Response parameters:\n\n") formatTable(out, []string{"Name", "Type", "Destination", "Optional", "Description"}, rows) } } fmt.Fprintf(out, "\n#### Responses:\n\n") var results [][]string if rez.Expected != "OK" { e := rez.Expected s := "" if e != "NO_CONTENT" { s = annotate(registry, rez.Type) } results = append(results, []string{rdl.StatusCode(e) + " " + rdl.StatusMessage(e), s}) } else { results = append(results, []string{"200 " + rdl.StatusMessage("OK"), string(rez.Type)}) } if len(rez.Alternatives) > 0 { for _, v := range rez.Alternatives { s := "" if v != "NO_CONTENT" { s = annotate(registry, rez.Type) } results = append(results, []string{rdl.StatusCode(v) + " " + rdl.StatusMessage(v), s}) } } fmt.Fprintf(out, "Expected:\n\n") formatTable(out, []string{"Code", "Type"}, results) if len(rez.Exceptions) > 0 { var rows [][]string for ec, edef := range rez.Exceptions { etype := edef.Type et := annotate(registry, rdl.TypeRef(etype)) ecomment := edef.Comment row := []string{rdl.StatusCode(ec) + " " + rdl.StatusMessage(ec), et, ecomment} rows = append(rows, row) } if rows != nil { sort.Sort(byCode(rows)) fmt.Fprintf(out, "\nException:\n\n") formatTable(out, []string{"Code", "Type", "Comment"}, rows) } } }