code generator for mock API server
parent
278b70fc3b
commit
950c2ad3c3
@ -0,0 +1,174 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bmatcuk/doublestar"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/bradrydzewski/togo/template"
|
||||
)
|
||||
|
||||
type (
|
||||
httptestParams struct {
|
||||
Package string
|
||||
Routes []*httptestRoute
|
||||
}
|
||||
httptestRoute struct {
|
||||
Source string
|
||||
Method string
|
||||
Path string
|
||||
Body string
|
||||
Status int
|
||||
Header map[string]string
|
||||
}
|
||||
)
|
||||
|
||||
var httptestCommand = cli.Command{
|
||||
Name: "httptest",
|
||||
Usage: "generate httptest server",
|
||||
Action: httptestAction,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "package",
|
||||
Value: "testdata",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "input",
|
||||
Value: "files/**",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "output",
|
||||
Value: "testdata_gen.go",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "exclude",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func httptestAction(c *cli.Context) error {
|
||||
pattern := c.Args().First()
|
||||
if pattern == "" {
|
||||
pattern = c.String("input")
|
||||
}
|
||||
|
||||
matches, err := doublestar.Glob(pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var exclude *regexp.Regexp
|
||||
if s := c.String("exclude"); s != "" {
|
||||
exclude = regexp.MustCompilePOSIX(s)
|
||||
}
|
||||
|
||||
params := httptestParams{
|
||||
Package: c.String("package"),
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
stat, oserr := os.Stat(match)
|
||||
if oserr != nil {
|
||||
return oserr
|
||||
}
|
||||
if stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if exclude != nil && exclude.MatchString(match) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("parsing file %s", match)
|
||||
|
||||
raw, ioerr := ioutil.ReadFile(match)
|
||||
if ioerr != nil {
|
||||
return ioerr
|
||||
}
|
||||
|
||||
route, parseErr := parseRoute(raw)
|
||||
if parseErr != nil {
|
||||
return parseErr
|
||||
}
|
||||
route.Source = strings.TrimPrefix(match, "files/")
|
||||
|
||||
params.Routes = append(params.Routes, route)
|
||||
}
|
||||
|
||||
wr := os.Stdout
|
||||
if output := c.String("output"); output != "" {
|
||||
wr, err = os.Create(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wr.Close()
|
||||
}
|
||||
|
||||
return template.Execute(wr, "httptest.tmpl", ¶ms)
|
||||
}
|
||||
|
||||
func parseRoute(in []byte) (*httptestRoute, error) {
|
||||
out := new(httptestRoute)
|
||||
out.Header = map[string]string{}
|
||||
|
||||
buf := bufio.NewReader(bytes.NewBuffer(in))
|
||||
r := textproto.NewReader(buf)
|
||||
|
||||
//
|
||||
// parses the method and path
|
||||
//
|
||||
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid request line. Want <method> <path>.")
|
||||
}
|
||||
out.Method = parts[0]
|
||||
out.Path = parts[1]
|
||||
|
||||
//
|
||||
// parses the mime headers
|
||||
//
|
||||
|
||||
header, err := r.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
for k, v := range header {
|
||||
out.Header[k] = strings.Join(v, "; ")
|
||||
}
|
||||
|
||||
//
|
||||
// extracts the response status code
|
||||
//
|
||||
|
||||
out.Status, err = strconv.Atoi(header.Get("Status"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid Status code. %s", err)
|
||||
}
|
||||
delete(out.Header, "Status")
|
||||
|
||||
//
|
||||
// parse the remainder of the file as the body
|
||||
//
|
||||
|
||||
body, err := ioutil.ReadAll(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.Body = string(body)
|
||||
|
||||
return out, nil
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
GET /user
|
||||
Status: 200
|
||||
Date: Mon, 01 Jul 2013 17:27:06 GMT
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-RateLimit-Limit: 60
|
||||
X-RateLimit-Remaining: 56
|
||||
X-RateLimit-Reset: 1372700873
|
||||
|
||||
{
|
||||
"login": "octocat",
|
||||
"id": 1,
|
||||
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/octocat",
|
||||
"html_url": "https://github.com/octocat",
|
||||
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false,
|
||||
"name": "monalisa octocat",
|
||||
"company": "GitHub",
|
||||
"blog": "https://github.com/blog",
|
||||
"location": "San Francisco",
|
||||
"email": "octocat@github.com",
|
||||
"hireable": false,
|
||||
"bio": "There once was...",
|
||||
"public_repos": 2,
|
||||
"public_gists": 1,
|
||||
"followers": 20,
|
||||
"following": 0,
|
||||
"created_at": "2008-01-14T04:33:35Z",
|
||||
"updated_at": "2008-01-14T04:33:35Z"
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
PATCH /user
|
||||
Status: 200
|
||||
Date: Mon, 01 Jul 2013 17:27:06 GMT
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-RateLimit-Limit: 60
|
||||
X-RateLimit-Remaining: 56
|
||||
X-RateLimit-Reset: 1372700873
|
||||
|
||||
{
|
||||
"login": "octocat",
|
||||
"id": 1,
|
||||
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/octocat",
|
||||
"html_url": "https://github.com/octocat",
|
||||
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false,
|
||||
"name": "monalisa octocat",
|
||||
"company": "GitHub",
|
||||
"blog": "https://github.com/blog",
|
||||
"location": "San Francisco",
|
||||
"email": "octocat@github.com",
|
||||
"hireable": false,
|
||||
"bio": "There once was...",
|
||||
"public_repos": 2,
|
||||
"public_gists": 1,
|
||||
"followers": 20,
|
||||
"following": 0,
|
||||
"created_at": "2008-01-14T04:33:35Z",
|
||||
"updated_at": "2008-01-14T04:33:35Z",
|
||||
"total_private_repos": 100,
|
||||
"owned_private_repos": 100,
|
||||
"private_gists": 81,
|
||||
"disk_usage": 10000,
|
||||
"collaborators": 8,
|
||||
"two_factor_authentication": true,
|
||||
"plan": {
|
||||
"name": "Medium",
|
||||
"space": 400,
|
||||
"private_repos": 20,
|
||||
"collaborators": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package httptest
|
||||
|
||||
//go:generate togo httptest -package httptest -output httptest_gen.go
|
@ -0,0 +1,84 @@
|
||||
package httptest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
// NewServer starts a new mock http.Server using the test data.
|
||||
func NewServer() *httptest.Server {
|
||||
return httptest.NewServer(
|
||||
http.HandlerFunc(router),
|
||||
)
|
||||
}
|
||||
|
||||
func router(w http.ResponseWriter, r *http.Request) {
|
||||
for _, route := range routes {
|
||||
if route.Method == r.Method && route.Path == r.URL.Path {
|
||||
w.WriteHeader(route.Status)
|
||||
route.Header.Write(w)
|
||||
io.WriteString(w, route.Body)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var routes = []struct {
|
||||
Method string
|
||||
Path string
|
||||
Body string
|
||||
Status int
|
||||
Header http.Header
|
||||
}{
|
||||
|
||||
// GET /user
|
||||
{
|
||||
Method: "GET",
|
||||
Path: "/user",
|
||||
Status: 200,
|
||||
Body: "{\n \"login\": \"octocat\",\n \"id\": 1,\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/octocat\",\n \"html_url\": \"https://github.com/octocat\",\n \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n \"type\": \"User\",\n \"site_admin\": false,\n \"name\": \"monalisa octocat\",\n \"company\": \"GitHub\",\n \"blog\": \"https://github.com/blog\",\n \"location\": \"San Francisco\",\n \"email\": \"octocat@github.com\",\n \"hireable\": false,\n \"bio\": \"There once was...\",\n \"public_repos\": 2,\n \"public_gists\": 1,\n \"followers\": 20,\n \"following\": 0,\n \"created_at\": \"2008-01-14T04:33:35Z\",\n \"updated_at\": \"2008-01-14T04:33:35Z\"\n}\n",
|
||||
Header: map[string][]string{
|
||||
"Content-Type": {
|
||||
"application/json; charset=utf-8",
|
||||
},
|
||||
"Date": {
|
||||
"Mon, 01 Jul 2013 17:27:06 GMT",
|
||||
},
|
||||
"X-Ratelimit-Limit": {
|
||||
"60",
|
||||
},
|
||||
"X-Ratelimit-Remaining": {
|
||||
"56",
|
||||
},
|
||||
"X-Ratelimit-Reset": {
|
||||
"1372700873",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// PATCH /user
|
||||
{
|
||||
Method: "PATCH",
|
||||
Path: "/user",
|
||||
Status: 200,
|
||||
Body: "{\n \"login\": \"octocat\",\n \"id\": 1,\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/octocat\",\n \"html_url\": \"https://github.com/octocat\",\n \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n \"type\": \"User\",\n \"site_admin\": false,\n \"name\": \"monalisa octocat\",\n \"company\": \"GitHub\",\n \"blog\": \"https://github.com/blog\",\n \"location\": \"San Francisco\",\n \"email\": \"octocat@github.com\",\n \"hireable\": false,\n \"bio\": \"There once was...\",\n \"public_repos\": 2,\n \"public_gists\": 1,\n \"followers\": 20,\n \"following\": 0,\n \"created_at\": \"2008-01-14T04:33:35Z\",\n \"updated_at\": \"2008-01-14T04:33:35Z\",\n \"total_private_repos\": 100,\n \"owned_private_repos\": 100,\n \"private_gists\": 81,\n \"disk_usage\": 10000,\n \"collaborators\": 8,\n \"two_factor_authentication\": true,\n \"plan\": {\n \"name\": \"Medium\",\n \"space\": 400,\n \"private_repos\": 20,\n \"collaborators\": 0\n }\n}",
|
||||
Header: map[string][]string{
|
||||
"Content-Type": {
|
||||
"application/json; charset=utf-8",
|
||||
},
|
||||
"Date": {
|
||||
"Mon, 01 Jul 2013 17:27:06 GMT",
|
||||
},
|
||||
"X-Ratelimit-Limit": {
|
||||
"60",
|
||||
},
|
||||
"X-Ratelimit-Remaining": {
|
||||
"56",
|
||||
},
|
||||
"X-Ratelimit-Reset": {
|
||||
"1372700873",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
// NewServer starts a new mock http.Server using the test data.
|
||||
func NewServer() *httptest.Server {
|
||||
return httptest.NewServer(
|
||||
http.HandlerFunc(router),
|
||||
)
|
||||
}
|
||||
|
||||
func router(w http.ResponseWriter, r *http.Request) {
|
||||
for _, route := range routes {
|
||||
if route.Method == r.Method && route.Path == r.URL.Path {
|
||||
w.WriteHeader(route.Status)
|
||||
route.Header.Write(w)
|
||||
io.WriteString(w, route.Body)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var routes = []struct {
|
||||
Method string
|
||||
Path string
|
||||
Body string
|
||||
Status int
|
||||
Header http.Header
|
||||
}{
|
||||
{{ range $index, $route := .Routes }}
|
||||
// {{ $route.Method }} {{ $route.Path }}
|
||||
{
|
||||
Method: {{ printf "%q" $route.Method }},
|
||||
Path: {{ printf "%q" $route.Path }},
|
||||
Status: {{ $route.Status }},
|
||||
{{ if $route.Body -}}
|
||||
Body: {{ printf "%q" $route.Body }},
|
||||
{{ end -}}
|
||||
Header: map[string][]string{
|
||||
{{ range $key, $value := $route.Header -}}
|
||||
{{ printf "%q" $key }} : []string{
|
||||
{{ printf "%q" $value }},
|
||||
},
|
||||
{{ end }}
|
||||
},
|
||||
},
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
{{/*
|
||||
//
|
||||
// http responses
|
||||
//
|
||||
|
||||
{{ range $index, $route := .Routes }}
|
||||
{{ if $route.Body -}}
|
||||
// {{ $route.Method }} {{ $route.Path }}
|
||||
var response{{ $index }} = `{{ $route.Body }}`
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
*/}}
|
Loading…
Reference in New Issue