A => LICENSE.txt +27 -0
@@ 0,0 1,27 @@
+Copyright (c) 2014, Sean E. Russell
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.))")""""""
M README.md +5 -0
@@ 17,3 17,8 @@ Example
will generate a Go library called "petstore" which can be imported and used to
interact with the Swagger sample application.
+
+License
+-------
+
+This software is released under the BSD Simplified license.
M api.go +19 -13
@@ 73,7 73,7 @@ type ResponseMessage struct {
type DataTypeFields struct {
Type string
- Ref string `json:"$ref"`
+ Ref string `json:"$ref"`
Format string
DefaultValue interface{}
Enum []string
@@ 113,9 113,9 @@ func (p DataTypeFields) typeOf() string
case "boolean":
case "array":
if p.Items["type"] != "" {
- return "[]"+p.Items["type"]
+ return "[]" + p.Items["type"]
} else {
- return "[]"+p.Items["$ref"]
+ return "[]" + p.Items["$ref"]
}
default:
if p.Ref != "" {
@@ 127,25 127,31 @@ func (p DataTypeFields) typeOf() string
func (p DataTypeFields) zero() string {
switch p.Type {
- case "integer": return "0"
- case "number": return "0.0"
+ 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{}"
+ default:
+ return `""`
+ case "byte":
+ return "[]byte{}"
+ case "date", "date-time":
+ return "time.Time{}"
}
- case "boolean": return "false"
+ case "boolean":
+ return "false"
case "array":
if p.Items["type"] != "" {
- return "[]"+p.Items["type"]+"{}"
+ return "[]" + p.Items["type"] + "{}"
} else {
- return "[]"+p.Items["$ref"]+"{}"
+ return "[]" + p.Items["$ref"] + "{}"
}
default:
if p.Ref != "" {
- return p.Ref+"{}"
+ return p.Ref + "{}"
}
- return p.Type+"{}"
+ return p.Type + "{}"
}
}
M cmd/sashay/main.go +1 -1
@@ 1,9 1,9 @@
package main
import (
+ "bitbucket.org/seanerussell/sashay"
"fmt"
"io/ioutil"
- "bitbucket.org/seanerussell/sashay"
)
func main() {
A => delete.go +5 -0
@@ 0,0 1,5 @@
+package sashay
+
+func genDelete(api Api, op Operation, out chan string) {
+ genGetBase(api, op, out)
+}
M generator.go +30 -83
@@ 16,17 16,20 @@ func Generate(pack string, j []byte, out
out <- fmt.Sprintf("package %s", pack)
out <- ""
out <- "import ("
+ out <- "\t\"bytes\""
out <- "\t\"encoding/json\""
out <- "\t\"errors\""
out <- "\t\"fmt\""
+ out <- "\t\"io\""
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)
+ case ApiDecl:
+ generateApi(t.(ApiDecl), out)
+ case ResourceListing:
+ generateResourceListing(t.(ResourceListing), out)
}
return nil
}
@@ 35,9 38,9 @@ func generateApi(a ApiDecl, out chan str
out <- fmt.Sprintf(`const basePath = "%s"`, a.BasePath)
out <- ""
generateModels(a, out)
- np := len(a.Apis)-1
+ np := len(a.Apis) - 1
for i, api := range a.Apis {
- no := len(api.Operations)-1
+ no := len(api.Operations) - 1
for j, op := range api.Operations {
generateDocs(op, out)
generateFunc(api, op, out)
@@ 66,35 69,37 @@ func generateModels(a ApiDecl, out chan
}
func generateDocs(op Operation, out chan string) {
- out <- "/*"
- out <- op.Summary
- out <- ""
+ out <- "/*******************************************************************************"
+ out <- " * " + op.Summary
+ out <- " *"
if op.Notes != "" {
- out <- op.Notes
- out <- ""
+ out <- " * " + op.Notes
+ out <- " *"
}
- out <- "Parameters:"
+ out <- " * Parameters:"
for _, p := range op.Parameters {
- out <- fmt.Sprintf("\t%s: %s", p.Name, p.Description)
+ out <- fmt.Sprintf(" *\t%s: %s", p.Name, p.Description)
}
- out <- "*/"
+ 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))
+ s := fmt.Sprintf("func %s(%s) %s {", fname, genArgs(op), genRv(op))
out <- s
switch op.Method {
case "DELETE":
- // FIXME
- //genDelete(api.Path, op.Nickname, )
+ genDelete(api, op, out)
out <- "\treturn nil"
case "PATCH":
// FIXME
+ out <- "\t// IMPLEMENT PATCH, YOU SILLY GOOSE"
out <- "\treturn nil, nil"
case "POST":
- out <- "\treturn nil"
+ genPost(api, op, out)
case "PUT":
+ // FIXME
+ out <- "\t// IMPLEMENT PUT, ALREADY. SHEESH."
out <- "\treturn nil"
case "GET":
genGet(api, op, out)
@@ 102,65 107,6 @@ func generateFunc(api Api, op Operation,
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)
@@ 200,7 146,7 @@ func genDeserialize(op Operation, out ch
func genArgs(op Operation) string {
b := bytes.Buffer{}
- np := len(op.Parameters)-1
+ np := len(op.Parameters) - 1
for i, p := range op.Parameters {
b.WriteString(p.Name)
b.WriteString(" ")
@@ 213,21 159,22 @@ func genArgs(op Operation) string {
}
func genRv(op Operation) string {
+ t := op.Type
switch op.Type {
- case "void": return ""
+ case "void":
+ return "error"
case "array":
if op.Items["$ref"] != "" {
- return "[]"+op.Items["$ref"]+", "
+ t = "[]" + op.Items["$ref"]
} else if op.Items["type"] != "" {
- return "[]"+op.Items["type"]+", "
+ t = "[]" + op.Items["type"]
} else {
panic("Implement me.")
}
}
- return op.Type+", "
+ return fmt.Sprintf("(%s, error)", t)
}
-
func generateResourceListing(r ResourceListing, out chan string) {
}
@@ 240,5 187,5 @@ func upcase(s string) string {
}
head := s[0:1]
tail := s[1:len(s)]
- return strings.ToUpper(head)+tail
+ return strings.ToUpper(head) + tail
}
A => get.go +77 -0
@@ 0,0 1,77 @@
+package sashay
+
+import (
+ "fmt"
+ "strings"
+)
+
+func genGetBase(api Api, op Operation, out chan string) {
+ rv := ""
+ zero := ""
+ if op.Type != "void" {
+ rv = "rv, "
+ zero = 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 <- fmt.Sprintf("\t\treturn %serrors.New(fmt.Sprintf(\"invalid value (%%d < %s)\", %s))", rv, p.Minimum, p.Name)
+ out <- "\t}"
+ }
+ if p.Maximum != "" {
+ out <- fmt.Sprintf("\tif %s > %s(%s) {", p.Name, p.typeOf(), p.Maximum)
+ out <- fmt.Sprintf("\t\treturn %serrors.New(fmt.Sprintf(\"invalid value (%%d > %s)\", %s))", rv, 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 <- "\tclient := &http.Client{}"
+ out <- fmt.Sprintf("\treq, err := http.NewRequest(%#v, url, nil)", op.Method)
+ out <- "\tresp, err := client.Do(req)"
+ out <- "\tif err != nil {"
+ out <- fmt.Sprintf("\t\treturn %serr", zero)
+ out <- "\t}"
+ for _, c := range op.ResponseMessages {
+ out <- fmt.Sprintf("\tif resp.StatusCode == %d {", c.Code)
+ out <- fmt.Sprintf("\t\treturn %serrors.New(%#v)", rv, c.Message)
+ out <- "\t}"
+ }
+}
+
+func genGet(api Api, op Operation, out chan string) {
+ out <- fmt.Sprintf("\trv := %s", op.zero())
+ genGetBase(api, op, out)
+ genDeserialize(op, out)
+}
A => post.go +85 -0
@@ 0,0 1,85 @@
+package sashay
+
+import (
+ "fmt"
+ "strings"
+)
+
+func genPostBase(api Api, op Operation, out chan string) {
+ rv := ""
+ zero := ""
+ if op.Type != "void" {
+ rv = "rv, "
+ zero = op.zero() + ", "
+ }
+ format := fmt.Sprintf(`"%%s%s`, api.Path)
+ formatArgs := []string{"basePath"}
+ params := make(map[string]string)
+ hasBody := false
+ for _, p := range op.Parameters {
+ if p.Minimum != "" {
+ out <- fmt.Sprintf("\tif %s < %s(%s) {", p.Name, p.typeOf(), p.Minimum)
+ out <- fmt.Sprintf("\t\treturn %serrors.New(fmt.Sprintf(\"invalid value (%%d < %s)\", %s))", rv, p.Minimum, p.Name)
+ out <- "\t}"
+ }
+ if p.Maximum != "" {
+ out <- fmt.Sprintf("\tif %s > %s(%s) {", p.Name, p.typeOf(), p.Maximum)
+ out <- fmt.Sprintf("\t\treturn %serrors.New(fmt.Sprintf(\"invalid value (%%d > %s)\", %s))", rv, 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 if p.ParamType == "body" {
+ hasBody = true
+ out <- fmt.Sprintf("\tb, e := json.Marshal(%s)", p.Name)
+ out <- "\tif e != nil {"
+ out <- "\t\treturn e"
+ out <- "\t}"
+ } else {
+ v := p.Name
+ if p.Type == "array" {
+ v = genArrayToString(p)
+ }
+ params[p.Name] = v
+ }
+ }
+ format += `"`
+ // FIXME for the case of body AND params
+ if len(params) > 0 && !hasBody {
+ out <- "\tm := make(map[string]interface{})"
+ for n, v := range params {
+ out <- fmt.Sprintf("\tm[%#v] = %s", n, v)
+ }
+ out <- "\tb, e := json.Marshal(m)"
+ out <- "\tif e != nil {"
+ out <- "\t\treturn e"
+ out <- "\t}"
+ }
+ url := "\turl := fmt.Sprintf(" + format
+ for _, v := range formatArgs {
+ url += ", " + v
+ }
+ url += ")"
+ out <- url
+ out <- "\tclient := &http.Client{}"
+ out <- fmt.Sprintf("\treq, err := http.NewRequest(%#v, url, bytes.NewReader(b))", op.Method)
+ out <- "\tresp, err := client.Do(req)"
+ out <- "\tif err != nil {"
+ out <- fmt.Sprintf("\t\treturn %serr", zero)
+ out <- "\t}"
+ for _, c := range op.ResponseMessages {
+ out <- fmt.Sprintf("\tif resp.StatusCode == %d {", c.Code)
+ out <- fmt.Sprintf("\t\treturn %serrors.New(%#v)", rv, c.Message)
+ out <- "\t}"
+ }
+ out <- "\tif resp.StatusCode >= 500 && resp.StatusCode < 600 {"
+ out <- fmt.Sprintf("\t\treturn %serrors.New(\"Internal server error\")", rv)
+ out <- "\t}"
+ out <- "\treturn nil"
+}
+
+func genPost(api Api, op Operation, out chan string) {
+ genPostBase(api, op, out)
+}
M swagger_test.go +8 -6
@@ 1,15 1,18 @@
-package sashay
+package sashay
+
import (
- "testing"
+ "fmt"
+ . "gopkg.in/check.v1"
"io/ioutil"
- "fmt"
"os"
- . "gopkg.in/check.v1"
+ "testing"
)
// Boiler plate for check
func Test(t *testing.T) { TestingT(t) }
-type MySuite struct {}
+
+type MySuite struct{}
+
var _ = Suite(&MySuite{})
var resourceListing, apiDocs []byte
@@ 73,6 76,5 @@ func (s *MySuite) TestParse(c *C) {
func (s *MySuite) TestGen(c *C) {
}
-
// http://petstore.swagger.wordnik.com/api/api-docs
// http://petstore.swagger.wordnik.com/api/api-docs/pet
M util.go +11 -6
@@ 9,16 9,19 @@ import (
)
const (
- UNDEFINED = -1
+ 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)
+ 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")
}
@@ 29,8 32,10 @@ func GetType(d []byte) (int, error) {
return UNDEFINED, e
}
switch t.(type) {
- case ApiDecl: return API, nil
- case ResourceListing: return RESOURCELIST, nil
+ case ApiDecl:
+ return API, nil
+ case ResourceListing:
+ return RESOURCELIST, nil
}
return UNDEFINED, errors.New("json document not recognized")
}