M api.go +71 -4
@@ 66,14 66,14 @@ type Parameter struct {
}
type ResponseMessage struct {
- code int
- message string
- responseModel string
+ Code int
+ Message string
+ ResponseModel string
}
type DataTypeFields struct {
Type string
- Ref string
+ Ref string `json:"$ref"`
Format string
DefaultValue interface{}
Enum []string
@@ 82,3 82,70 @@ type DataTypeFields struct {
Items map[string]string
UniqueItems bool
}
+
+func (p DataTypeFields) typeOf() string {
+ switch p.Type {
+ case "File":
+ return "io.Reader"
+ case "integer":
+ if p.Format == "int64" {
+ return p.Format
+ } else {
+ return "int32"
+ }
+ case "number":
+ if p.Format == "double" {
+ return "float64"
+ } else {
+ return "float32"
+ }
+ case "string":
+ switch p.Format {
+ default:
+ return "string"
+ case "byte":
+ return "[]byte"
+ case "date":
+ return "date.Time"
+ case "date-time":
+ return "date.Time"
+ }
+ case "boolean":
+ case "array":
+ if p.Items["type"] != "" {
+ return "[]"+p.Items["type"]
+ } else {
+ return "[]"+p.Items["$ref"]
+ }
+ default:
+ if p.Ref != "" {
+ return p.Ref
+ }
+ }
+ return p.Type
+}
+
+func (p DataTypeFields) zero() string {
+ switch p.Type {
+ case "integer": return "0"
+ case "number": return "0.0"
+ case "string":
+ switch p.Format {
+ default: return `""`
+ case "byte": return "[]byte{}"
+ case "date", "date-time": return "time.Time{}"
+ }
+ case "boolean": return "false"
+ case "array":
+ if p.Items["type"] != "" {
+ return "[]"+p.Items["type"]+"{}"
+ } else {
+ return "[]"+p.Items["$ref"]+"{}"
+ }
+ default:
+ if p.Ref != "" {
+ return p.Ref+"{}"
+ }
+ return p.Type+"{}"
+ }
+}
A => cmd/sashay/main.go +22 -0
@@ 0,0 1,22 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "bitbucket.org/seanerussell/sashay"
+)
+
+func main() {
+ b, _ := ioutil.ReadFile("test/api-docs_pet.json")
+ c := make(chan string)
+ wait := make(chan bool)
+ go func() {
+ for i := range c {
+ fmt.Println(i)
+ }
+ close(wait)
+ }()
+ sashay.Generate("test", b, c)
+ close(c)
+ <-wait
+}
A => generator.go +244 -0
@@ 0,0 1,244 @@
+package sashay
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+var swaggerToGo map[string]string
+
+func Generate(pack string, j []byte, out chan string) error {
+ t, e := Parse(j)
+ if e != nil {
+ return e
+ }
+ out <- fmt.Sprintf("package %s", pack)
+ out <- ""
+ out <- "import ("
+ out <- "\t\"encoding/json\""
+ out <- "\t\"errors\""
+ out <- "\t\"fmt\""
+ out <- "\t\"io/ioutil\""
+ out <- "\t\"io\""
+ out <- "\t\"net/http\""
+ out <- ")"
+ out <- ""
+ switch t.(type) {
+ case ApiDecl: generateApi(t.(ApiDecl), out)
+ case ResourceListing: generateResourceListing(t.(ResourceListing), out)
+ }
+ return nil
+}
+
+func generateApi(a ApiDecl, out chan string) {
+ out <- fmt.Sprintf(`const basePath = "%s"`, a.BasePath)
+ out <- ""
+ generateModels(a, out)
+ np := len(a.Apis)-1
+ for i, api := range a.Apis {
+ no := len(api.Operations)-1
+ for j, op := range api.Operations {
+ generateDocs(op, out)
+ generateFunc(api, op, out)
+ if j < no {
+ out <- ""
+ }
+ }
+ if i < np {
+ out <- ""
+ }
+ }
+}
+
+func generateModels(a ApiDecl, out chan string) {
+ for _, m := range a.Models {
+ out <- fmt.Sprintf("type %s struct {", upcase(m.Id))
+ for pk, pv := range m.Properties {
+ if pv.Description != "" {
+ out <- fmt.Sprintf("\t// %s", pv.Description)
+ }
+ out <- fmt.Sprintf("\t%s %s", upcase(pk), pv.typeOf())
+ }
+ out <- "}"
+ out <- ""
+ }
+}
+
+func generateDocs(op Operation, out chan string) {
+ out <- "/*"
+ out <- op.Summary
+ out <- ""
+ if op.Notes != "" {
+ out <- op.Notes
+ out <- ""
+ }
+ out <- "Parameters:"
+ for _, p := range op.Parameters {
+ out <- fmt.Sprintf("\t%s: %s", p.Name, p.Description)
+ }
+ out <- "*/"
+}
+
+func generateFunc(api Api, op Operation, out chan string) {
+ fname := upcase(op.Nickname)
+ s := fmt.Sprintf("func %s(%s) (%serror) {", fname, genArgs(op), genRv(op))
+ out <- s
+ switch op.Method {
+ case "DELETE":
+ // FIXME
+ //genDelete(api.Path, op.Nickname, )
+ out <- "\treturn nil"
+ case "PATCH":
+ // FIXME
+ out <- "\treturn nil, nil"
+ case "POST":
+ out <- "\treturn nil"
+ case "PUT":
+ out <- "\treturn nil"
+ case "GET":
+ genGet(api, op, out)
+ }
+ out <- "}"
+}
+
+func genGet(api Api, op Operation, out chan string) {
+ out <- fmt.Sprintf("\trv := %s", op.zero())
+ format := fmt.Sprintf(`"%%s%s`, api.Path)
+ formatArgs := []string{"basePath"}
+ params := make(map[string]string)
+ for _, p := range op.Parameters {
+ if p.Minimum != "" {
+ out <- fmt.Sprintf("\tif %s < %s(%s) {", p.Name, p.typeOf(), p.Minimum)
+ out <- "\t\treturn rv, errors.New(fmt.Sprintf(\"invalid value (%d < "+p.Minimum+")\", "+p.Name+"))"
+ out <- "\t}"
+ }
+ if p.Maximum != "" {
+ out <- fmt.Sprintf("\tif %s > %s(%s) {", p.Name, p.typeOf(), p.Maximum)
+ out <- "\t\treturn rv, errors.New(fmt.Sprintf(\"invalid value (%d > "+p.Maximum+")\", "+p.Name+"))"
+ out <- "\t}"
+ }
+ match := fmt.Sprintf("{%s}", p.Name)
+ if strings.Contains(format, match) {
+ format = strings.Replace(format, match, "%v", 1)
+ formatArgs = append(formatArgs, p.Name)
+ } else {
+ v := p.Name
+ if p.Type == "array" {
+ v = genArrayToString(p)
+ }
+ params[p.Name] = v
+ }
+ }
+ if len(params) > 0 {
+ format += "?"
+ l := len(params)-1
+ c := 0
+ for n, v := range params {
+ format = format + n + "=%s"
+ formatArgs = append(formatArgs, v)
+ if c < l {
+ format += "&"
+ }
+ }
+ }
+ format += `"`
+ url := "\turl := fmt.Sprintf("+format
+ for _, v := range formatArgs {
+ url += ", "+v
+ }
+ url += ")"
+ out <- url
+ out <- "\tresp, err := http.Get(url)"
+ out <- "\tif err != nil {"
+ out <- fmt.Sprintf("\t\treturn %s, err", op.zero())
+ out <- "\t}"
+ for _, c := range op.ResponseMessages {
+ out <- fmt.Sprintf("\tif resp.StatusCode == %d {", c.Code)
+ out <- fmt.Sprintf("\t\treturn rv, errors.New(\"%s\")", c.Message)
+ out <- "\t}"
+ }
+ genDeserialize(op, out)
+}
+
+func genArrayToString(p Parameter) string {
+ rv := bytes.Buffer{}
+ xs := fmt.Sprintf("%ss", p.Name)
+ rv.WriteString(fmt.Sprintf("\t%s := []string{}", xs))
+ rv.WriteString(fmt.Sprintf("\tfor _, v := range %s {", p.Name))
+ rv.WriteString(fmt.Sprintf("\t\t%s = append(%s, toString(v))", xs, xs))
+ rv.WriteString("\t}")
+ rv.WriteString(fmt.Sprintf("\t%sString := strings.Join(%s, \"%2C\")", p.Name, xs))
+ return string(rv.Bytes())
+}
+
+func genDeserialize(op Operation, out chan string) {
+ out <- "\tdefer resp.Body.Close()"
+ out <- "\ts, e := ioutil.ReadAll(resp.Body)"
+ out <- "\tif e != nil {"
+ out <- fmt.Sprintf("\t\treturn %s, e", op.zero())
+ out <- "\t}"
+ switch op.typeOf() {
+ case "string":
+ out <- "\treturn s, nil"
+ case "int":
+ out <- "\treturn strconv.Atoi(s)"
+ case "int64":
+ out <- "\tk, e := strconv.ParseInt(s, 10, 64)"
+ out <- "\tif e != nil {"
+ out <- fmt.Sprintf("\t\treturn %s, e", op.zero())
+ out <- "\t}"
+ out <- "\treturn k, nil"
+ default:
+ out <- "\te = json.Unmarshal(s, &rv)"
+ out <- "\tif e != nil {"
+ out <- fmt.Sprintf("\t\treturn %s, e", op.zero())
+ out <- "\t}"
+ out <- "\treturn rv, nil"
+ }
+}
+
+func genArgs(op Operation) string {
+ b := bytes.Buffer{}
+ np := len(op.Parameters)-1
+ for i, p := range op.Parameters {
+ b.WriteString(p.Name)
+ b.WriteString(" ")
+ b.WriteString(p.typeOf())
+ if i < np {
+ b.WriteString(", ")
+ }
+ }
+ return string(b.Bytes())
+}
+
+func genRv(op Operation) string {
+ switch op.Type {
+ case "void": return ""
+ case "array":
+ if op.Items["$ref"] != "" {
+ return "[]"+op.Items["$ref"]+", "
+ } else if op.Items["type"] != "" {
+ return "[]"+op.Items["type"]+", "
+ } else {
+ panic("Implement me.")
+ }
+ }
+ return op.Type+", "
+}
+
+
+func generateResourceListing(r ResourceListing, out chan string) {
+}
+
+func upcase(s string) string {
+ if len(s) == 0 {
+ return s
+ }
+ if len(s) == 1 {
+ return strings.ToUpper(s)
+ }
+ head := s[0:1]
+ tail := s[1:len(s)]
+ return strings.ToUpper(head)+tail
+}
M resource_listing.go +1 -0
@@ 23,6 23,7 @@ type Authorization struct {
Scopes []Scope
GrantTypes GrantTypes
}
+
type Resource struct {
Path string
Description string
M swagger_test.go +18 -2
@@ 1,5 1,4 @@
-package sashay
-
+package sashay
import (
"testing"
"io/ioutil"
@@ 53,6 52,23 @@ func (s *MySuite) TestParseApi(c *C) {
c.Assert(k.Apis[0].Operations[1].Produces[0], Equals, "application/json")
}
+func (s *MySuite) TestType(c *C) {
+ p, e := GetType(apiDocs)
+ c.Assert(e, IsNil)
+ c.Assert(p, Equals, API)
+ q, e := GetType(resourceListing)
+ c.Assert(e, IsNil)
+ c.Assert(q, Equals, RESOURCELIST)
+}
+
+func (s *MySuite) TestParse(c *C) {
+ r, e := Parse(apiDocs)
+ c.Assert(e, IsNil)
+ c.Assert(r, FitsTypeOf, ApiDecl{})
+ t, e := Parse(resourceListing)
+ c.Assert(e, IsNil)
+ c.Assert(t, FitsTypeOf, ResourceListing{})
+}
func (s *MySuite) TestGen(c *C) {
}
A => test/main.go +24 -0
@@ 0,0 1,24 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "flag"
+ "bitbucket.org/seanerussell/sashay"
+ "os"
+)
+
+func main() {
+ flag.Parse()
+ if flag.NArg() != 1 {
+ fmt.Println("USAGE: %s <json>", os.Args[0])
+ return
+ }
+ b, e := ioutil.ReadFile(flag.Args()[0])
+ if e != nil {
+ fmt.Printf("error reading %s: %s", flag.Args()[0], e.Error())
+ os.Exit(1)
+ }
+ v, e := sashay.Parse(b)
+ fmt.Printf("%+v\n", v)
+}
M util.go +48 -0
@@ 5,8 5,56 @@ package sashay
import (
"encoding/json"
+ "errors"
)
+const (
+ UNDEFINED = -1
+ RESOURCELIST = iota
+ API
+)
+
+func Parse(d []byte) (interface{}, error) {
+ switch t, e := GetType(d); {
+ case e != nil: return nil, e
+ case t == API: return ParseApi(d)
+ case t == RESOURCELIST: return ParseResource(d)
+ }
+ return nil, errors.New("unrecognized json format")
+}
+
+func GetType(d []byte) (int, error) {
+ t, e := getStruct(d)
+ if e != nil {
+ return UNDEFINED, e
+ }
+ switch t.(type) {
+ case ApiDecl: return API, nil
+ case ResourceListing: return RESOURCELIST, nil
+ }
+ return UNDEFINED, errors.New("json document not recognized")
+}
+
+func getStruct(d []byte) (interface{}, error) {
+ var listing ResourceListing
+ e := json.Unmarshal(d, &listing)
+ if e != nil {
+ return nil, e
+ }
+ if listing.Info != (Info{}) {
+ return listing, nil
+ }
+ var api ApiDecl
+ e = json.Unmarshal(d, &api)
+ if e != nil {
+ return nil, e
+ }
+ if api.ResourcePath != "" {
+ return api, nil
+ }
+ return nil, errors.New("json document not recognized")
+}
+
func ParseResource(j []byte) (ResourceListing, error) {
var r ResourceListing
err := json.Unmarshal(j, &r)